diff --git a/Makefile b/Makefile index 53ec70f42..8f484a74f 100644 --- a/Makefile +++ b/Makefile @@ -146,8 +146,9 @@ include third_party/dlmalloc/dlmalloc.mk #─┘ include libc/mem/mem.mk #─┐ include third_party/gdtoa/gdtoa.mk # ├──DYNAMIC RUNTIME include third_party/nsync/mem/mem.mk # │ You can now use stdio -include libc/thread/thread.mk # │ You can finally call malloc() -include tool/hello/hello.mk # │ +include libc/proc/proc.mk # │ You can now use threads +include libc/thread/thread.mk # │ You can now use processes +include tool/hello/hello.mk # │ You can finally call malloc() include third_party/zlib/zlib.mk # │ include libc/stdio/stdio.mk # │ include libc/time/time.mk # │ @@ -243,6 +244,7 @@ include test/libc/xed/test.mk include test/libc/fmt/test.mk include test/libc/dns/test.mk include test/libc/time/test.mk +include test/libc/proc/test.mk include test/libc/stdio/test.mk include test/libc/release/test.mk include test/libc/test.mk @@ -331,6 +333,7 @@ COSMOPOLITAN_OBJECTS = \ THIRD_PARTY_GDTOA \ THIRD_PARTY_REGEX \ LIBC_THREAD \ + LIBC_PROC \ THIRD_PARTY_NSYNC_MEM \ LIBC_MEM \ THIRD_PARTY_DLMALLOC \ @@ -376,6 +379,7 @@ COSMOPOLITAN_HEADERS = \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ + LIBC_PROC \ THIRD_PARTY_NSYNC \ THIRD_PARTY_XED \ LIBC_STR \ diff --git a/ape/ape-m1.c b/ape/ape-m1.c index 5488357d9..ba07d178d 100644 --- a/ape/ape-m1.c +++ b/ape/ape-m1.c @@ -33,7 +33,7 @@ #define pagesz 16384 #define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24) -#define SYSLIB_VERSION 2 +#define SYSLIB_VERSION 4 struct Syslib { int magic; @@ -59,6 +59,15 @@ struct Syslib { /* v2 (2023-09-10) */ pthread_t (*pthread_self)(void); void (*dispatch_release)(dispatch_semaphore_t); + int (*raise)(int); + int (*pthread_join)(pthread_t, void **); + void (*pthread_yield_np)(void); + int pthread_stack_min; + int sizeof_pthread_attr_t; + int (*pthread_attr_init)(pthread_attr_t *); + int (*pthread_attr_destroy)(pthread_attr_t *); + int (*pthread_attr_setstacksize)(pthread_attr_t *, size_t); + int (*pthread_attr_setguardsize)(pthread_attr_t *, size_t); }; #define ELFCLASS32 1 @@ -834,6 +843,15 @@ int main(int argc, char **argv, char **envp) { M->lib.dispatch_walltime = dispatch_walltime; M->lib.pthread_self = pthread_self; M->lib.dispatch_release = dispatch_release; + M->lib.raise = raise; + M->lib.pthread_join = pthread_join; + M->lib.pthread_yield_np = pthread_yield_np; + M->lib.pthread_stack_min = PTHREAD_STACK_MIN; + M->lib.sizeof_pthread_attr_t = sizeof(pthread_attr_t); + M->lib.pthread_attr_init = pthread_attr_init; + M->lib.pthread_attr_destroy = pthread_attr_destroy; + M->lib.pthread_attr_setstacksize = pthread_attr_setstacksize; + M->lib.pthread_attr_setguardsize = pthread_attr_setguardsize; /* getenv("_") is close enough to at_execfn */ execfn = argc > 0 ? argv[0] : 0; diff --git a/ape/ape.S b/ape/ape.S index 348f21912..75050c0cf 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -775,30 +775,19 @@ ape_loader_end: .stub ape_ram_memsz,quad .stub ape_ram_align,quad -// APE Stack Configuration -// -// We actually respect this when allocating a deterministically -// addressed stack on Windows and other operating systems. However -// there's a few caveats: -// -// - If ape_stack_pf has PF_X then OpenBSD probably won't work -// - We don't bother creating a deterministic stack in MODE=tiny -// -// With that said, here's some examples of configuration: -// -// STATIC_SYMBOL("ape_stack_pf", "7"); // RWX -// STATIC_STACK_ADDR(0x6fffffff0000); -// STATIC_STACK_SIZE(65536); -// -// @see ape.lds for defaults +// These values are left empty because some UNIX OSes give p_filesz +// priority over `ulimit -s` a.k.a. RLIMIT_STACK which is preferred +// because we use an 8mb stack by default so that decadent software +// doesn't unexpectedly crash, but putting that here meant NetBSD's +// rusage accounting (which is the best) always reported 6mb of RSS .long PT_GNU_STACK - .stub ape_stack_pf,long // override w/ PF_X for execstack - .stub ape_stack_offset,quad // ignored - .stub ape_stack_vaddr,quad // is mmap()'d with MAP_FIXED - .stub ape_stack_paddr,quad // ignored - .stub ape_stack_filesz,quad // ignored - .stub ape_stack_memsz,quad // ignored? - .stub ape_stack_align,quad // must be 16+ + .stub ape_stack_pf,long + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .stub ape_stack_align,quad #if SupportsOpenbsd() || SupportsNetbsd() .long PT_NOTE @@ -1086,7 +1075,7 @@ ape_pe: .ascin "PE",4 .short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console .short v_ntdllchar // DllCharacteristics .quad 0x10000 // StackReserve - .quad 0x10000 // StackCommit + .quad 0x1000 // StackCommit .quad 0 // HeapReserve .quad 0 // HeapCommit .long 0 // LoaderFlags diff --git a/bin/cosmoaddr2line b/bin/cosmoaddr2line new file mode 100755 index 000000000..07d8bc886 --- /dev/null +++ b/bin/cosmoaddr2line @@ -0,0 +1,15 @@ +#!/bin/sh +set -- -apifCe "$@" + +if [ -n "$ADDR2LINE" ]; then + exec "$ADDR2LINE" "$@" +fi + +COSMO=${COSMO:-/opt/cosmo} +for ARCH in x86_64 aarch64; do + o/third_party/gcc/bin/$ARCH-linux-musl-addr2line "$@" 2>/dev/null && exit + "$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-addr2line" "$@" 2>/dev/null && exit +done + +echo please set the ADDR2LINE environment variable >&2 +exit 1 diff --git a/build/definitions.mk b/build/definitions.mk index 21552bc23..450e98dd0 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -86,7 +86,7 @@ IGNORE := $(shell $(MKDIR) $(TMPDIR)) ifneq ($(findstring aarch64,$(MODE)),) ARCH = aarch64 -HOSTS ?= pi silicon +HOSTS ?= pi silicon-wifi else ARCH = x86_64 HOSTS ?= freebsd rhel7 rhel5 xnu win10 openbsd netbsd diff --git a/build/online.mk b/build/online.mk index 82b94e7a3..ffe5d6b86 100644 --- a/build/online.mk +++ b/build/online.mk @@ -25,7 +25,10 @@ .PRECIOUS: o/$(MODE)/%.com.ok o/$(MODE)/%.com.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet o/$(MODE)/%.com.ok: \ - o//tool/build/runit.com \ - o//tool/build/runitd.com \ + o/$(MODE)/tool/build/runit.com \ + o/$(MODE)/tool/build/runitd.com \ o/$(MODE)/%.com - @$(COMPILE) -wATEST -tT$@ $^ $(HOSTS) + $(COMPILE) -wATEST -tT$@ $^ $(HOSTS) + +.PHONY: +o/tiny/tool/build/runit.com: diff --git a/dsp/core/illumination.c b/dsp/core/illumination.c index 7bf7b9636..f534c78d7 100644 --- a/dsp/core/illumination.c +++ b/dsp/core/illumination.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "dsp/core/core.h" #include "dsp/core/illumination.h" +#include "dsp/core/core.h" #include "libc/str/str.h" const double kIlluminantA[3] = {1.0985, 1, 0.35585}; diff --git a/dsp/mpeg/mpeg1.c b/dsp/mpeg/mpeg1.c index 1822c6432..ff874c42c 100644 --- a/dsp/mpeg/mpeg1.c +++ b/dsp/mpeg/mpeg1.c @@ -1080,12 +1080,12 @@ static plm_frame_t *plm_video_decode_impl(plm_video_t *self) { } plm_frame_t *plm_video_decode(plm_video_t *self) { - long double tsc; plm_frame_t *res; + struct timespec tsc; INFOF("plm_video_decode"); - tsc = nowl(); + tsc = timespec_real(); res = plm_video_decode_impl(self); - plmpegdecode_latency_ = lroundl((nowl() - tsc) * 1e6l); + plmpegdecode_latency_ = timespec_tomicros(timespec_sub(timespec_real(), tsc)); return res; } diff --git a/dsp/tty/hidecursor.c b/dsp/tty/hidecursor.c index be3460e9c..40d04b01e 100644 --- a/dsp/tty/hidecursor.c +++ b/dsp/tty/hidecursor.c @@ -32,7 +32,7 @@ static int ttysetcursor(int fd, bool visible) { char code[8] = "\e[?25l"; if (__nocolor) return 0; if (visible) code[5] = 'h'; - if (SupportsWindows()) { + if (IsWindows()) { GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor); ntcursor.bVisible = visible; SetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor); diff --git a/examples/symtab.c b/examples/abort.c similarity index 55% rename from examples/symtab.c rename to examples/abort.c index 66ee4bace..e6b1e64da 100644 --- a/examples/symtab.c +++ b/examples/abort.c @@ -7,36 +7,8 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" -/** - * @fileoverview example of how to embed symbol table in .com file - * - * # build our binary - * make -j16 o//examples/symtab.com - * - * # move binary somewhere else - * # so it can't find the .com.dbg binary - * cp o//examples/symtab.com /tmp - * - * # run program - * # notice that it has a symbolic backtrace - * /tmp/symtab.com - * - * @see examples/examples.mk - */ - int main(int argc, char *argv[]) { - - // this links all the debugging and zip functionality - ShowCrashReports(); - - kprintf("----------------\n"); - kprintf(" THIS IS A TEST \n"); - kprintf("SIMULATING CRASH\n"); - kprintf("----------------\n"); - - volatile int64_t x; - return 1 / (x = 0); + abort(); } diff --git a/examples/crashreport.c b/examples/crashreport.c index 6c3d8a6ba..7ea9a5e81 100644 --- a/examples/crashreport.c +++ b/examples/crashreport.c @@ -11,6 +11,7 @@ #include "libc/math.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" +#include "libc/stdio/stdio.h" /** * @fileoverview How to print backtraces and cpu state on crash. @@ -38,6 +39,7 @@ dontubsan int main(int argc, char *argv[]) { volatile double c = exp(b) / a; (void)c; - volatile int64_t x; - return 1 / (x = 0); + volatile int x = 0; + volatile int y = 1 / x; + return y; } diff --git a/examples/die.c b/examples/die.c new file mode 100644 index 000000000..c2d13cbb0 --- /dev/null +++ b/examples/die.c @@ -0,0 +1,14 @@ +#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/log/log.h" + +int main(int argc, char *argv[]) { + __die(); +} diff --git a/examples/examples.mk b/examples/examples.mk index 41d2cbc28..9c30c18a3 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -55,6 +55,7 @@ EXAMPLES_DIRECTDEPS = \ LIBC_NT_NTDLL \ LIBC_NT_USER32 \ LIBC_NT_WS2_32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ @@ -146,25 +147,6 @@ o/$(MODE)/examples/nesemu1.com.dbg: \ $(EXAMPLES_BOOTLOADER) @$(APELINK) -# # force symtab.com to be a zip file, by pulling a zip asset into linkage -# # we wouldn't need to do this if we depended on functions like localtime -# o/$(MODE)/examples/symtab.com.dbg: \ -# $(EXAMPLES_DEPS) \ -# o/$(MODE)/examples/symtab.o \ -# o/$(MODE)/examples/symtab.c.zip.o \ -# o/$(MODE)/examples/examples.pkg \ -# $(EXAMPLES_BOOTLOADER) -# @$(APELINK) - -# modify .com so it can read the symbol table without needing the .com.dbg file -o/$(MODE)/examples/symtab.com: \ - o/$(MODE)/examples/symtab.com.dbg \ - o/$(MODE)/third_party/zip/zip.com \ - o/$(MODE)/tool/build/symtab.com - @$(MAKE_OBJCOPY) - @$(MAKE_SYMTAB_CREATE) - @$(MAKE_SYMTAB_ZIP) - o/$(MODE)/examples/picol.o: private \ CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/examples/greenbean.c b/examples/greenbean.c index e67925056..3ba5cd21b 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -27,6 +27,7 @@ #include "libc/sock/struct/sockaddr.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" @@ -116,9 +117,11 @@ void *Worker(void *id) { server = socket(AF_INET, SOCK_STREAM, 0); if (server == -1) { - kprintf("socket() failed %m\n" - " try running: sudo prlimit --pid=$$ --nofile=%d\n", - threads * 2); + kprintf("\r\e[Ksocket() failed %m\n"); + if (errno == ENFILE || errno == EMFILE) { + TooManyFileDescriptors: + kprintf("sudo prlimit --pid=$$ --nofile=%d\n", threads * 3); + } goto WorkerFinished; } @@ -136,7 +139,7 @@ void *Worker(void *id) { // possible for our many threads to bind to the same interface! // otherwise we'd need to create a complex multi-threaded queue if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - kprintf("%s() failed %m\n", "socket"); + kprintf("\r\e[Ksocket() returned %m\n"); goto CloseWorker; } unassert(!listen(server, 1)); @@ -148,7 +151,7 @@ void *Worker(void *id) { uint32_t clientaddrsize; struct sockaddr_in clientaddr; int client, inmsglen, outmsglen; - char inbuf[1500], outbuf[512], *p, *q; + char inbuf[512], outbuf[512], *p, *q; // musl libc and cosmopolitan libc support a posix thread extension // that makes thread cancellation work much better your io routines @@ -165,15 +168,14 @@ void *Worker(void *id) { // turns cancellation off so we don't interrupt active http clients unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0)); - // accept() can raise a very diverse number of errors but none of - // them are really true showstoppers that would necessitate us to - // panic and abort the entire server, so we can just ignore these if (client == -1) { - // we used SO_RCVTIMEO and SO_SNDTIMEO because those settings are - // inherited by the accepted sockets, but using them also has the - // side-effect that the listening socket fails with EAGAIN errors - // which are harmless, and so are most other errors accept raises - // e.g. ECANCELED, which lets us check closingtime without delay! + if (errno != EAGAIN && errno != ECANCELED) { + kprintf("\r\e[Kaccept() returned %m\n"); + if (errno == ENFILE || errno == EMFILE) { + goto TooManyFileDescriptors; + } + usleep(10000); + } continue; } @@ -301,7 +303,7 @@ int main(int argc, char *argv[]) { // print cpu registers and backtrace on crash // note that pledge'll makes backtraces worse - // you can press ctrl+\ to trigger your crash + // you can press ctrl+\ to trigger backtraces ShowCrashReports(); // listen for ctrl-c, terminal close, and kill @@ -341,10 +343,6 @@ int main(int argc, char *argv[]) { // the server will be allowed to use. this way if it gets hacked, they // won't be able to do much damage, like compromising the whole server // - // we use an internal api to force threads to enable beforehand, since - // cosmopolitan code morphs the binary to support tls across platforms - // and doing that requires extra permissions we don't need for serving - // // pledge violations on openbsd are logged nicely to the system logger // but on linux we need to use a cosmopolitan extension to get details // although doing that slightly weakens the security pledge() provides @@ -353,21 +351,22 @@ int main(int argc, char *argv[]) { // is too old, then pledge() and unveil() don't consider this an error // so it works. if security is critical there's a special call to test // which is npassert(!pledge(0, 0)), and npassert(unveil("", 0) != -1) - __enable_threads(); - __pledge_mode = PLEDGE_PENALTY_KILL_THREAD | PLEDGE_STDERR_LOGGING; + __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; // c. greenbean --strace unveil("/dev/null", "rw"); unveil(0, 0); pledge("stdio inet", 0); - // spawn over 9,000 worker threads + // initialize our synchronization data structures, which were written + // by mike burrows in a library called *nsync we've tailored for libc + unassert(!pthread_cond_init(&statuscond, 0)); + unassert(!pthread_mutex_init(&statuslock, 0)); + + // spawn over 9000 worker threads // // you don't need weird i/o models, or event driven yoyo pattern code // to build a massively scalable server. the secret is to use threads // with tiny stacks. then you can write plain simple imperative code! // - // we like pthread attributes since they generally make thread spawns - // faster especially in cases where you need to make detached threads - // // we block signals in our worker threads so we won't need messy code // to spin on eintr. operating systems also deliver signals to random // threads, and we'd have ctrl-c, etc. be handled by the main thread. @@ -375,17 +374,15 @@ int main(int argc, char *argv[]) { // alternatively you can just use signal() instead of sigaction(); it // uses SA_RESTART because all the syscalls the worker currently uses // are documented as @restartable which means no EINTR toil is needed - unassert(!pthread_cond_init(&statuscond, 0)); - unassert(!pthread_mutex_init(&statuslock, 0)); sigset_t block; - sigfillset(&block); - sigdelset(&block, SIGSEGV); // invalid memory access - sigdelset(&block, SIGBUS); // another kind of bad memory access - sigdelset(&block, SIGFPE); // divide by zero, etc. - sigdelset(&block, SIGSYS); // pledge violations - sigdelset(&block, SIGILL); // bad cpu opcode + sigemptyset(&block); + sigaddset(&block, SIGINT); + sigaddset(&block, SIGHUP); + sigaddset(&block, SIGQUIT); pthread_attr_t attr; unassert(!pthread_attr_init(&attr)); + unassert(!pthread_attr_setguardsize(&attr, 4096)); + unassert(!pthread_attr_setstacksize(&attr, 65536)); unassert(!pthread_attr_setsigmask_np(&attr, &block)); pthread_t *th = gc(calloc(threads, sizeof(pthread_t))); for (i = 0; i < threads; ++i) { @@ -393,10 +390,10 @@ int main(int argc, char *argv[]) { ++a_workers; if ((rc = pthread_create(th + i, &attr, Worker, (void *)(intptr_t)i))) { --a_workers; - // rc will most likely be EAGAIN (we hit the process/thread limit) - kprintf("\r\e[Kerror: pthread_create(%d) failed: %s\n" - " try increasing RLIMIT_NPROC\n", - i, strerror(rc)); + kprintf("\r\e[Kpthread_create failed: %s\n", strerror(rc)); + if (rc == EAGAIN) { + kprintf("sudo prlimit --pid=$$ --nproc=%d\n", threads * 2); + } if (!i) exit(1); threads = i; break; diff --git a/examples/linenoise.c b/examples/linenoise.c index 6f767664f..f615f6306 100644 --- a/examples/linenoise.c +++ b/examples/linenoise.c @@ -13,7 +13,7 @@ #include "libc/intrin/kprintf.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/sig.h" diff --git a/examples/nanosleep.c b/examples/nanosleep.c deleted file mode 100644 index 565ab8e68..000000000 --- a/examples/nanosleep.c +++ /dev/null @@ -1,61 +0,0 @@ -#if 0 -/*─────────────────────────────────────────────────────────────────╗ -│ To the extent possible under law, Justine Tunney has waived │ -│ all copyright and related or neighboring rights to this file, │ -│ as it is written in the following disclaimers: │ -│ • http://unlicense.org/ │ -│ • http://creativecommons.org/publicdomain/zero/1.0/ │ -╚─────────────────────────────────────────────────────────────────*/ -#endif -#include "libc/calls/calls.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/time/time.h" -#include "libc/x/x.h" - -/** - * 16kb sleep executable that runs on all operating systems. - * https://justine.lol/cosmopolitan/demos/sleep.c - * https://justine.lol/cosmopolitan/demos/sleep.com - * https://justine.lol/cosmopolitan/index.html - */ - -#define WRITE(s) write(2, s, strlen(s)) - -int main(int argc, char *argv[]) { - char *p; - long double x, y; - if (argc != 2) { - WRITE("Usage: "); - WRITE(argv[0]); - WRITE(" SECONDS\n"); - exit(1); - } - x = 0; - for (p = argv[1]; isdigit(*p); ++p) { - x *= 10; - x += *p - '0'; - } - if (*p == '.') { - y = 1; - for (++p; isdigit(*p); ++p) { - x += (*p - '0') * (y /= 10); - } - } - switch (*p) { - case 'm': - x *= 60; - break; - case 'h': - x *= 60 * 60; - break; - case 'd': - x *= 60 * 60 * 24; - break; - default: - break; - } - dsleep(x); - return 0; -} diff --git a/examples/nesemu1.cc b/examples/nesemu1.cc index 2bf6a4245..a1d436d16 100644 --- a/examples/nesemu1.cc +++ b/examples/nesemu1.cc @@ -40,6 +40,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/thread.h" #include "libc/time/time.h" #include "libc/x/xasprintf.h" #include "libc/x/xsigaction.h" @@ -249,7 +250,7 @@ void Exit(int rc) { void Cleanup(void) { ttyraw((enum TtyRawFlags)(-1u)); ttyshowcursor(STDOUT_FILENO); - if (playpid_) kill(playpid_, SIGTERM), sched_yield(); + if (playpid_) kill(playpid_, SIGTERM), pthread_yield(); } void OnTimer(void) { diff --git a/examples/spawn_bench.c b/examples/spawn_bench.c new file mode 100644 index 000000000..b04f1bc2f --- /dev/null +++ b/examples/spawn_bench.c @@ -0,0 +1,139 @@ +#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/atomic.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/calls/weirdtypes.h" +#include "libc/mem/mem.h" +#include "libc/proc/posix_spawn.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" + +#define ITERATIONS 10 + +_Alignas(128) int a; +_Alignas(128) int b; +_Alignas(128) atomic_int lock; + +static struct timespec SubtractTime(struct timespec a, struct timespec b) { + a.tv_sec -= b.tv_sec; + if (a.tv_nsec < b.tv_nsec) { + a.tv_nsec += 1000000000; + a.tv_sec--; + } + a.tv_nsec -= b.tv_nsec; + return a; +} + +static time_t ToNanoseconds(struct timespec ts) { + return ts.tv_sec * 1000000000 + ts.tv_nsec; +} + +static char *Ithoa(char p[27], unsigned long x) { + char m[26]; + unsigned i; + i = 0; + do { + m[i++] = x % 10 + '0'; + x = x / 10; + } while (x); + for (;;) { + *p++ = m[--i]; + if (!i) break; + if (!(i % 3)) *p++ = ','; + } + *p = '\0'; + return p; +} + +#define BENCH(name, x) \ + { \ + int i; \ + char ibuf[27]; \ + struct timespec t1, t2; \ + clock_gettime(CLOCK_REALTIME, &t1); \ + for (i = 0; i < ITERATIONS; ++i) { \ + x; \ + } \ + clock_gettime(CLOCK_REALTIME, &t2); \ + Ithoa(ibuf, ToNanoseconds(SubtractTime(t2, t1)) / ITERATIONS); \ + printf("%-50s %16s ns\n", name, ibuf); \ + } + +void PosixSpawnWait(const char *prog) { + int ws, err, pid; + char *args[] = {(char *)prog, 0}; + char *envs[] = {0}; + if ((err = posix_spawn(&pid, prog, NULL, NULL, args, envs))) { + perror(prog); + exit(1); + } + if (waitpid(pid, &ws, 0) == -1) { + perror("waitpid"); + exit(1); + } + if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { + puts("bad exit status"); + exit(1); + } +} + +void ForkExecWait(const char *prog) { + int ws; + if (!fork()) { + execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0}); + _Exit(127); + } + if (wait(&ws) == -1) { + perror("wait"); + exit(1); + } + if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { + puts("bad exit status"); + exit(1); + } +} + +char *FillMemory(char *p, long n) { + return memset(p, -1, n); +} +char *(*pFillMemory)(char *, long) = FillMemory; + +int main(int argc, char *argv[]) { + long n; + void *p; + const char *prog; + + if (argc <= 1) { + prog = "tiny64"; + } else { + prog = argv[1]; + } + + BENCH("fork() + exec(tiny)", ForkExecWait(prog)); + BENCH("posix_spawn(tiny)", PosixSpawnWait(prog)); + + n = 128L * 1024 * 1024; + p = pFillMemory( + mmap(0, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), n); + BENCH("mmap(128mb, MAP_SHARED) + fork() + exec(tiny)", ForkExecWait(prog)); + BENCH("mmap(128mb, MAP_SHARED) + posix_spawn(tiny)", PosixSpawnWait(prog)); + munmap(p, n); + + n = 128L * 1024 * 1024; + p = pFillMemory(malloc(n), n); + BENCH("malloc(128mb) + fork() + exec(tiny)", ForkExecWait(prog)); + BENCH("malloc(128mb) + posix_spawn(tiny)", PosixSpawnWait(prog)); + free(p); +} diff --git a/examples/stackoverflow.c b/examples/stackoverflow.c index adffaa9c0..7a97d130c 100644 --- a/examples/stackoverflow.c +++ b/examples/stackoverflow.c @@ -8,6 +8,7 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/limits.h" #include "libc/log/check.h" #include "libc/runtime/runtime.h" @@ -21,8 +22,6 @@ #define N INT_MAX -STATIC_STACK_SIZE(FRAMESIZE); - int A(int f(), int n) { if (n < N) { return f(f, n + 1) - 1; @@ -34,6 +33,10 @@ int A(int f(), int n) { int (*Ap)(int (*)(), int) = A; int main(int argc, char *argv[]) { + if (IsWindows()) { + fprintf(stderr, "stack overflow not possible to catch on windows yet\n"); + exit(1); + } ShowCrashReports(); return !!Ap(Ap, 0); } diff --git a/examples/nanosleep_test.c b/examples/tib.c similarity index 50% rename from examples/nanosleep_test.c rename to examples/tib.c index d6d8e70c5..1411d845c 100644 --- a/examples/nanosleep_test.c +++ b/examples/tib.c @@ -7,37 +7,13 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/assert.h" -#include "libc/calls/struct/timespec.h" #include "libc/stdio/stdio.h" -#include "libc/sysv/consts/clock.h" +#include "libc/thread/tls.h" -/** - * @fileoverview shows how accurate clock_nanosleep() is - */ +void *hog(void) { + return &__get_tls()->tib_errno; +} int main(int argc, char *argv[]) { - long i, ns; - struct timespec x, y; - timespec_sleep(timespec_fromnanos(0)); // warmup - - printf("\nrelative sleep\n"); - for (i = 0; i < 28; ++i) { - ns = 1l << i; - x = timespec_real(); - timespec_sleep(timespec_fromnanos(ns)); - y = timespec_real(); - printf("%,11ld ns sleep took %,ld ns\n", ns, - timespec_tonanos(timespec_sub(y, x))); - } - - printf("\nabsolute sleep\n"); - for (i = 0; i < 28; ++i) { - ns = 1l << i; - x = timespec_real(); - timespec_sleep_until(timespec_add(x, timespec_fromnanos(ns))); - y = timespec_real(); - printf("%,11ld ns sleep took %,ld ns\n", ns, - timespec_tonanos(timespec_sub(y, x))); - } + printf("%zu\n", sizeof(struct CosmoTib)); } diff --git a/examples/time.c b/examples/time.c deleted file mode 100644 index 3e44273ba..000000000 --- a/examples/time.c +++ /dev/null @@ -1,100 +0,0 @@ -#if 0 -/*─────────────────────────────────────────────────────────────────╗ -│ To the extent possible under law, Justine Tunney has waived │ -│ all copyright and related or neighboring rights to this file, │ -│ as it is written in the following disclaimers: │ -│ • http://unlicense.org/ │ -│ • http://creativecommons.org/publicdomain/zero/1.0/ │ -╚─────────────────────────────────────────────────────────────────*/ -#endif -#include "libc/time/time.h" -#include "libc/calls/calls.h" -#include "libc/calls/struct/timespec.h" -#include "libc/fmt/itoa.h" -#include "libc/limits.h" -#include "libc/log/log.h" -#include "libc/math.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/ex.h" -#include "libc/x/xspawn.h" - -/** - * @fileoverview command for showing how long a command takes - * - * This offers the following improvements over the existing `time` - * command that's incorporated into most shells: - * - * - This will show microseconds if seconds < 1 - * - This will launch APE binaries on systems with broken libc execv() - */ - -#define WRITE(FD, STR) write(FD, STR, strlen(STR)) - -void OnChild(void *arg) { - char **argv = arg; - execv(argv[0], argv); - _exit(127); -} - -long double GetTimeval(struct timeval t) { - return t.tv_sec + t.tv_usec * 1e-6l; -} - -void PrintMetric(const char *name, long double d) { - char buf[256], *p = buf; - long mins, secs, mils, mics; - mins = d / 60; - secs = fmodl(d, 60); - mils = fmodl(d * 1000, 1000); - mics = fmodl(d * 1000000, 1000); - p = stpcpy(p, name), *p++ = '\t'; - p = FormatInt64(p, mins), *p++ = 'm'; - p = FormatInt64(p, secs), *p++ = '.'; - *p++ = '0' + mils / 100; - *p++ = '0' + mils / 10 % 10; - *p++ = '0' + mils % 10; - if (!secs) { - *p++ = '0' + mics / 100; - *p++ = '0' + mics / 10 % 10; - *p++ = '0' + mics % 10; - } - *p++ = 's'; - *p++ = '\n'; - write(2, buf, p - buf); -} - -int main(int argc, char *argv[]) { - int ws; - char *exepath; - struct rusage r; - long double real; - char exebuf[PATH_MAX]; - if (argc >= 2) { - if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) { - real = nowl(); - argv[1] = exepath; - if ((ws = xvspawn(OnChild, argv + 1, &r)) != -1) { - PrintMetric("real", nowl() - real); - PrintMetric("user", GetTimeval(r.ru_utime)); - PrintMetric("sys", GetTimeval(r.ru_stime)); - if (WIFEXITED(ws)) { - return WEXITSTATUS(ws); - } else { - return 128 + WTERMSIG(ws); - } - } else { - perror("xvspawn"); - return 127; - } - } else { - perror(argv[1]); - return 127; - } - } else { - WRITE(2, "Usage: "); - WRITE(2, argv[0]); - WRITE(2, " PROG [ARGS...]\n"); - return EX_USAGE; - } -} diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index 11655a0c4..8a405da59 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -90,9 +90,9 @@ int rawmode(void) { perror("tcsetattr"); } - WRITE(1, ENABLE_SAFE_PASTE); - WRITE(1, ENABLE_MOUSE_TRACKING); - WRITE(1, PROBE_DISPLAY_SIZE); + /* WRITE(1, ENABLE_SAFE_PASTE); */ + /* WRITE(1, ENABLE_MOUSE_TRACKING); */ + /* WRITE(1, PROBE_DISPLAY_SIZE); */ return 0; } @@ -146,7 +146,7 @@ const char *describemouseevent(int e) { // change the code above to enable ISIG if you want to trigger this // then press ctrl-c or ctrl-\ in your pseudoteletypewriter console void OnSignalThatWontEintrRead(int sig) { - dprintf(1, "got %s\n", strsignal(sig)); + dprintf(1, "got %s (read()ing will SA_RESTART)\n", strsignal(sig)); } int main(int argc, char *argv[]) { @@ -170,6 +170,10 @@ int main(int argc, char *argv[]) { perror("read"); exit(1); } + if (!n) { + printf("got stdin eof\n"); + exit(0); + } printf("%`'.*s (got %d) ", n, code, n); if (iscntrl(code[0]) && !code[1]) { printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0])); diff --git a/libc/calls/__sig2.c b/libc/calls/__sig2.c deleted file mode 100644 index 8c60686f6..000000000 --- a/libc/calls/__sig2.c +++ /dev/null @@ -1,225 +0,0 @@ -/*-*- 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 "ape/sections.internal.h" -#include "libc/calls/internal.h" -#include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/siginfo.h" -#include "libc/calls/struct/ucontext.internal.h" -#include "libc/intrin/describebacktrace.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/log/libfatal.internal.h" -#include "libc/nt/console.h" -#include "libc/nt/enum/context.h" -#include "libc/nt/struct/context.h" -#include "libc/nt/thread.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" -#include "libc/thread/tls.h" - -#ifdef __x86_64__ - -/** - * Returns true if signal is ignored by default. - */ -textwindows bool __sig_is_ignored(int sig) { - return sig == SIGURG || // - sig == SIGCONT || // - sig == SIGCHLD || // - sig == SIGWINCH; -} - -/** - * Returns true if signal is so fatal it should dump core. - */ -textwindows bool __sig_is_core(int sig) { - return sig == SIGSYS || // - sig == SIGBUS || // - sig == SIGSEGV || // - sig == SIGQUIT || // - sig == SIGTRAP || // - sig == SIGXCPU || // - sig == SIGXFSZ; -} - -static inline textwindows int __sig_is_masked(int sig) { - if (__tls_enabled) { - return __get_tls()->tib_sigmask & (1ull << (sig - 1)); - } else { - return __sig.mask & (1ull << (sig - 1)); - } -} - -/** - * Delivers signal to callback. - * - * @return true if `EINTR` should be raised - */ -bool __sig_deliver(int sigops, int sig, int sic, ucontext_t *ctx) { - unsigned rva = __sighandrvas[sig]; - unsigned flags = __sighandflags[sig]; - - // generate expensive data if needed - ucontext_t uc; - siginfo_t info; - siginfo_t *infop; - if (flags & SA_SIGINFO) { - __repstosb(&info, 0, sizeof(info)); - info.si_signo = sig; - info.si_code = sic; - infop = &info; - if (!ctx) { - struct NtContext nc = {.ContextFlags = kNtContextAll}; - __repstosb(&uc, 0, sizeof(uc)); - GetThreadContext(GetCurrentThread(), &nc); - _ntcontext2linux(&uc, &nc); - ctx = &uc; - } - } else { - infop = 0; - } - - // save the thread's signal mask - uint64_t oldmask; - if (__tls_enabled) { - oldmask = __get_tls()->tib_sigmask; - } else { - oldmask = __sig.mask; - } - if (ctx) { - ctx->uc_sigmask = (sigset_t){{oldmask}}; - } - - // mask the signal that's being handled whilst handling - if (!(flags & SA_NODEFER)) { - if (__tls_enabled) { - __get_tls()->tib_sigmask |= 1ull << (sig - 1); - } else { - __sig.mask |= 1ull << (sig - 1); - } - } - - STRACE("delivering %G", sig); - ((sigaction_f)(__executable_start + rva))(sig, infop, ctx); - - if (ctx) { - oldmask = ctx->uc_sigmask.__bits[0]; - } - if (__tls_enabled) { - __get_tls()->tib_sigmask = oldmask; - } else { - __sig.mask = oldmask; - } - if (flags & SA_RESETHAND) { - __sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL; - } - - if (!(sigops & kSigOpRestartable)) { - return true; // always send EINTR for wait4(), poll(), etc. - } else if (flags & SA_RESTART) { - STRACE("restarting syscall on %G", sig); - return false; // resume syscall for read(), write(), etc. - } else { - return true; // default course is to raise EINTR - } -} - -/** - * Handles signal. - * @return true if `EINTR` should be raised - */ -textwindows bool __sig_handle(int sigops, int sig, int sic, ucontext_t *ctx) { - if (__sighandrvas[sig] == (intptr_t)SIG_IGN || - (__sighandrvas[sig] == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) { - return false; - } - if (__sig_is_masked(sig)) { - if (sigops & kSigOpUnmaskable) { - goto DefaultAction; - } - if (__tls_enabled) { - __get_tls()->tib_sigpending |= 1ull << (sig - 1); - } else { - __sig.pending |= 1ull << (sig - 1); - } - return false; - } - switch (__sighandrvas[sig]) { - case (intptr_t)SIG_DFL: - DefaultAction: - if (!__sig_is_ignored(sig)) { - uint32_t cmode; - intptr_t hStderr; - const char *signame; - char *end, sigbuf[21], output[123]; - signame = strsignal_r(sig, sigbuf); - STRACE("terminating due to uncaught %s", signame); - if (__sig_is_core(sig)) { - hStderr = GetStdHandle(kNtStdErrorHandle); - if (GetConsoleMode(hStderr, &cmode)) { - end = stpcpy(output, signame); - end = stpcpy(end, " "); - end = stpcpy( - end, - DescribeBacktrace( - ctx ? (struct StackFrame *)ctx->uc_mcontext.BP - : (struct StackFrame *)__builtin_frame_address(0))); - end = stpcpy(end, "\n"); - WriteFile(hStderr, output, end - output, 0, 0); - } - } - ExitProcess(sig); - } - // fallthrough - case (intptr_t)SIG_IGN: - return false; - default: - return __sig_deliver(sigops, sig, sic, ctx); - } -} - -static textwindows bool __sig_checkem(int sigops, uint64_t *pending) { - bool res = false; - for (int sig = 1; sig <= 64; ++sig) { - if (*pending & (1ull << (sig - 1))) { - *pending &= ~(1ull << (sig - 1)); - res |= __sig_handle(sigops, sig, SI_KERNEL, 0); - } - } - return res; -} - -/** - * Checks for unblocked signals and delivers them on New Technology. - * @return true if EINTR should be returned by caller - * @note called from main thread - * @threadsafe - */ -textwindows bool __sig_check(int sigops) { - bool res = false; - if (__tls_enabled) { - res |= __sig_checkem(sigops, &__get_tls()->tib_sigpending); - } - return res | __sig_checkem(sigops, &__sig.pending); -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/abort.c b/libc/calls/abort.c index 86ec337c5..cbe973e80 100644 --- a/libc/calls/abort.c +++ b/libc/calls/abort.c @@ -40,5 +40,5 @@ wontreturn void abort(void) { raise(SIGABRT); signal(SIGABRT, SIG_DFL); raise(SIGABRT); - notpossible; + _Exit(128 + SIGABRT); } diff --git a/libc/calls/bo.internal.h b/libc/calls/bo.internal.h index ffff39573..54fa7c933 100644 --- a/libc/calls/bo.internal.h +++ b/libc/calls/bo.internal.h @@ -1,25 +1,10 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_ -#include "libc/dce.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int begin_blocking_operation(void); -void end_blocking_operation(int); - -#if SupportsWindows() -#define BEGIN_BLOCKING_OPERATION \ - do { \ - int _Flags; \ - _Flags = begin_blocking_operation() -#define END_BLOCKING_OPERATION \ - end_blocking_operation(_Flags); \ - } \ - while (0) -#else #define BEGIN_BLOCKING_OPERATION (void)0 #define END_BLOCKING_OPERATION (void)0 -#endif COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 43e240d8c..c1f68f97e 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -209,6 +209,7 @@ int madvise(void *, uint64_t, int); #endif #ifdef _COSMO_SOURCE +bool32 fdexists(int); bool32 fileexists(const char *); bool32 isdirectory(const char *); bool32 isexecutable(const char *); @@ -232,8 +233,6 @@ int sys_ptrace(int, ...); int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t); int sys_ioprio_get(int, int); int sys_ioprio_set(int, int, int); -int tgkill(int, int, int); -int tkill(int, int); int tmpfd(void); int touch(const char *, unsigned); int unveil(const char *, const char *); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 8c7316407..23b558af2 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,53 +65,11 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) -# we can't use sanitizers because: -# we're on a stack owned by win32 without tls -o/$(MODE)/libc/calls/foist.o \ -o/$(MODE)/libc/calls/__sig2.o \ -o/$(MODE)/libc/calls/sigchld-nt.o \ -o/$(MODE)/libc/calls/sigwinch-nt.o \ -o/$(MODE)/libc/calls/onntconsoleevent.o \ -o/$(MODE)/libc/calls/wincrash.o \ -o/$(MODE)/libc/calls/ntcontext2linux.o: private \ +$(LIBC_CALLS_A_OBJS): private \ COPTS += \ - $(NO_MAGIC) - -# we can't use asan because: -# siginfo_t memory is owned by kernels -o/$(MODE)/libc/calls/siginfo2cosmo.o: private \ - COPTS += \ - -ffreestanding \ - -fno-sanitize=address - -# we can't use asan because: -# ucontext_t memory is owned by xnu kernel -o/$(MODE)/libc/calls/sigenter-xnu.o: private \ - COPTS += \ - -ffreestanding \ - -fno-sanitize=address - -# we can't use asan because: -# vdso memory is owned by linux kernel -o/$(MODE)/libc/calls/vdsofunc.greg.o: private \ - COPTS += \ - -ffreestanding \ - -fno-sanitize=address - -# we can't use magic because: -# this code is called by WinMain -o/$(MODE)/libc/calls/winstdin1.o: private \ - COPTS += \ - $(NO_MAGIC) - -# we can't use asan because: -# ntspawn allocates 128kb of heap memory via win32 -o/$(MODE)/libc/calls/ntspawn.o \ -o/$(MODE)/libc/calls/mkntcmdline.o \ -o/$(MODE)/libc/calls/mkntenvblock.o: private \ - COPTS += \ - -ffreestanding \ - -fno-sanitize=address + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 ifneq ($(ARCH), aarch64) # we always want -O3 because: @@ -129,21 +87,6 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: private \ -mstringop-strategy=loop endif -# we must disable static stack safety because: -# these functions use alloca(n) -o/$(MODE)/libc/calls/execl.o \ -o/$(MODE)/libc/calls/execle.o \ -o/$(MODE)/libc/calls/execlp.o \ -o/$(MODE)/libc/calls/execvpe.o \ -o/$(MODE)/libc/calls/statfs.o \ -o/$(MODE)/libc/calls/fstatfs.o \ -o/$(MODE)/libc/calls/execve-sysv.o \ -o/$(MODE)/libc/calls/readlinkat-nt.o \ -o/$(MODE)/libc/calls/execve-nt.greg.o \ -o/$(MODE)/libc/calls/mkntenvblock.o: private \ - CPPFLAGS += \ - -DSTACK_FRAME_UNLIMITED - # we always want -Os because: # va_arg codegen is very bloated in default mode o//libc/calls/open.o \ @@ -176,24 +119,6 @@ o/$(MODE)/libc/calls/timeval_frommicros.o: private \ CFLAGS += \ -O2 -# privileged functions -o/$(MODE)/libc/calls/sigenter-freebsd.o \ -o/$(MODE)/libc/calls/sigenter-netbsd.o \ -o/$(MODE)/libc/calls/sigenter-openbsd.o \ -o/$(MODE)/libc/calls/sigenter-linux.o \ -o/$(MODE)/libc/calls/sigenter-xnu.o \ -o/$(MODE)/libc/calls/pledge-linux.o \ -o/$(MODE)/libc/calls/siginfo2cosmo.o: private \ - CFLAGS += \ - -ffreestanding \ - -fno-sanitize=all \ - -fno-stack-protector - -o/$(MODE)/libc/calls/pledge-linux.o \ -o/$(MODE)/libc/calls/unveil.o: private \ - CFLAGS += \ - -DSTACK_FRAME_UNLIMITED - ifeq ($(ARCH), aarch64) o/$(MODE)/libc/calls/sigaction.o: private CFLAGS += -mcmodel=large o/$(MODE)/libc/calls/getloadavg-nt.o: private CFLAGS += -ffreestanding @@ -215,7 +140,7 @@ o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/calls/switchstacks.o: libc/calls/switchstacks.S +o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x))) diff --git a/libc/calls/chdir.c b/libc/calls/chdir.c index 7e2d4d031..9c1c13ccc 100644 --- a/libc/calls/chdir.c +++ b/libc/calls/chdir.c @@ -23,6 +23,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" +#include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/runtime/zipos.internal.h" #include "libc/sysv/errfuns.h" @@ -49,7 +50,15 @@ int chdir(const char *path) { int rc; struct ZiposUri zipname; - GetProgramExecutableName(); // XXX: ugly workaround + if (_weaken(__get_tmpdir)) { + _weaken(__get_tmpdir)(); + } + if (_weaken(GetAddr2linePath)) { + _weaken(GetAddr2linePath)(); + } + if (_weaken(GetProgramExecutableName)) { + _weaken(GetProgramExecutableName)(); + } if (!path || (IsAsan() && !__asan_is_valid_str(path))) { rc = efault(); } else if (_weaken(__zipos_parseuri) && diff --git a/libc/calls/clock_gettime-m1.c b/libc/calls/clock_gettime-m1.c index df9a01ae8..11b0e6568 100644 --- a/libc/calls/clock_gettime-m1.c +++ b/libc/calls/clock_gettime-m1.c @@ -22,5 +22,5 @@ #include "libc/runtime/syslib.internal.h" int sys_clock_gettime_m1(int clock, struct timespec *ts) { - return _sysret(__syslib->clock_gettime(clock, ts)); + return _sysret(__syslib->__clock_gettime(clock, ts)); } diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index b99b02e2f..5716e7ec8 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -77,18 +77,17 @@ * @vforksafe */ int clock_gettime(int clock, struct timespec *ts) { + // threads on win32 stacks call this so we can't asan check *ts int rc; if (clock == 127) { rc = einval(); // 127 is used by consts.sh to mean unsupported - } else if (!ts || (IsAsan() && !__asan_is_valid_timespec(ts))) { - rc = efault(); } else { rc = __clock_gettime(clock, ts); } #if SYSDEBUG if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) { - STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock), - DescribeTimespec(rc, ts), rc); + POLLTRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock), + DescribeTimespec(rc, ts), rc); } #endif return rc; diff --git a/libc/calls/clock_nanosleep-nt.c b/libc/calls/clock_nanosleep-nt.c index a61483c25..6385df6bd 100644 --- a/libc/calls/clock_nanosleep-nt.c +++ b/libc/calls/clock_nanosleep-nt.c @@ -16,43 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/bo.internal.h" #include "libc/calls/internal.h" -#include "libc/calls/sig.internal.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.internal.h" -#include "libc/macros.internal.h" +#include "libc/errno.h" #include "libc/nt/synchronization.h" #include "libc/sysv/consts/timer.h" -#include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" +#include "third_party/finger/finger.h" -static textwindows int sys_clock_nanosleep_nt_impl(int clock, int flags, - const struct timespec *req, - struct timespec *rem) { - struct timespec now, abs; - if (flags & TIMER_ABSTIME) { - abs = *req; - for (;;) { - if (sys_clock_gettime_nt(clock, &now)) return -1; - if (timespec_cmp(now, abs) >= 0) return 0; - if (_check_interrupts(0)) return -1; - SleepEx(MIN(__SIG_POLLING_INTERVAL_MS, - timespec_tomillis(timespec_sub(abs, now))), - false); - } - } else { +static textwindows int sys_clock_nanosleep_nt_impl(int clock, + struct timespec abs) { + struct timespec now; + struct PosixThread *pt = _pthread_self(); + for (;;) { if (sys_clock_gettime_nt(clock, &now)) return -1; - abs = timespec_add(now, *req); - for (;;) { - sys_clock_gettime_nt(clock, &now); - if (timespec_cmp(now, abs) >= 0) return 0; - if (_check_interrupts(0)) { - if (rem) *rem = timespec_sub(abs, now); - return -1; - } - SleepEx(MIN(__SIG_POLLING_INTERVAL_MS, - timespec_tomillis(timespec_sub(abs, now))), - false); + if (timespec_cmp(now, abs) >= 0) return 0; + if (_check_interrupts(0)) return -1; + pt->abort_errno = 0; + pt->pt_flags |= PT_INSEMAPHORE; + WaitForSingleObject(pt->semaphore, + timespec_tomillis(timespec_sub(abs, now))); + pt->pt_flags &= ~PT_INSEMAPHORE; + if (pt->abort_errno) { + errno = pt->abort_errno; + return -1; } } } @@ -61,8 +50,17 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags, const struct timespec *req, struct timespec *rem) { int rc; - BEGIN_BLOCKING_OPERATION; - rc = sys_clock_nanosleep_nt_impl(clock, flags, req, rem); - END_BLOCKING_OPERATION; + struct timespec abs, now; + if (flags & TIMER_ABSTIME) { + abs = *req; + } else { + if (sys_clock_gettime_nt(clock, &now)) return -1; + abs = timespec_add(now, *req); + } + rc = sys_clock_nanosleep_nt_impl(clock, abs); + if (rc == -1 && rem && errno == EINTR) { + sys_clock_gettime_nt(clock, &now); + *rem = timespec_subz(abs, now); + } return rc; } diff --git a/libc/calls/clock_nanosleep-xnu.c b/libc/calls/clock_nanosleep-xnu.c index f7da82749..d72adf207 100644 --- a/libc/calls/clock_nanosleep-xnu.c +++ b/libc/calls/clock_nanosleep-xnu.c @@ -21,12 +21,18 @@ #include "libc/calls/struct/timeval.h" #include "libc/calls/struct/timeval.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/errno.h" #include "libc/fmt/conv.h" +#include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" #include "libc/runtime/syslib.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/timer.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" +#include "libc/thread/tls.h" int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req, struct timespec *rem) { @@ -51,16 +57,25 @@ int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req, #else long res; struct timespec abs, now, rel; + if (_weaken(pthread_testcancel_np) && // + _weaken(pthread_testcancel_np)()) { + return ecanceled(); + } if (flags & TIMER_ABSTIME) { abs = *req; - if (!(res = __syslib->clock_gettime(clock, &now))) { + if (!(res = __syslib->__clock_gettime(clock, &now))) { if (timespec_cmp(abs, now) > 0) { rel = timespec_sub(abs, now); - res = __syslib->nanosleep(&rel, 0); + res = __syslib->__nanosleep(&rel, 0); } } } else { - res = __syslib->nanosleep(req, rem); + res = __syslib->__nanosleep(req, rem); + } + if (res == -EINTR && // + (_weaken(pthread_testcancel_np) && // + _weaken(pthread_testcancel_np))) { + return ecanceled(); } return _sysret(res); #endif diff --git a/libc/calls/clock_nanosleep.c b/libc/calls/clock_nanosleep.c index 01157780c..d18bf7695 100644 --- a/libc/calls/clock_nanosleep.c +++ b/libc/calls/clock_nanosleep.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/calls/asan.internal.h" #include "libc/calls/blockcancel.internal.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/clock_gettime.internal.h" #include "libc/calls/cp.internal.h" @@ -78,7 +79,7 @@ static struct timespec GetNanosleepLatency(void) { clock_gettime_f *cgt; struct timespec x, y, w = {0, 1}; if (!(nanos = g_nanosleep_latency)) { - BLOCK_CANCELLATIONS; + BLOCK_SIGNALS; for (cgt = __clock_gettime_get(0);;) { npassert(!cgt(CLOCK_REALTIME_PRECISE, &x)); rc = sys_clock_nanosleep(CLOCK_REALTIME, 0, &w, 0); @@ -90,7 +91,7 @@ static struct timespec GetNanosleepLatency(void) { break; } } - ALLOW_CANCELLATIONS; + ALLOW_SIGNALS; } return timespec_fromnanos(nanos); } @@ -117,7 +118,7 @@ static errno_t SpinNanosleep(int clock, int flags, const struct timespec *req, cgt = __clock_gettime_get(0); npassert(!cgt(CLOCK_REALTIME, &start)); for (;;) { - sched_yield(); + pthread_yield(); npassert(!cgt(CLOCK_REALTIME, &now)); if (flags & TIMER_ABSTIME) { if (timespec_cmp(now, *req) >= 0) { @@ -242,13 +243,11 @@ static bool ShouldUseSpinNanosleep(int clock, int flags, errno_t clock_nanosleep(int clock, int flags, const struct timespec *req, struct timespec *rem) { int rc; + // threads on win32 stacks call this so we can't asan check *ts LOCKTRACE("clock_nanosleep(%s, %s, %s) → ...", DescribeClockName(clock), DescribeSleepFlags(flags), DescribeTimespec(0, req)); if (IsMetal()) { rc = ENOSYS; - } else if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || - (rem && !__asan_is_valid_timespec(rem))))) { - rc = EFAULT; } else if (clock == 127 || // (flags & ~TIMER_ABSTIME) || // req->tv_sec < 0 || // diff --git a/libc/calls/copyfile.c b/libc/calls/copyfile.c index 53ff47d79..30e6d9b7d 100644 --- a/libc/calls/copyfile.c +++ b/libc/calls/copyfile.c @@ -29,6 +29,7 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/filetime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/madv.h" @@ -37,17 +38,24 @@ static textwindows int sys_copyfile_nt(const char *src, const char *dst, int flags) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + struct { + char16_t src16[PATH_MAX]; + char16_t dst16[PATH_MAX]; + } M; + CheckLargeStackAllocation(&M, sizeof(M)); +#pragma GCC pop_options int64_t fhsrc, fhdst; struct NtFileTime accessed, modified; - char16_t src16[PATH_MAX], dst16[PATH_MAX]; - if (__mkntpath(src, src16) == -1) return -1; - if (__mkntpath(dst, dst16) == -1) return -1; - if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) { + if (__mkntpath(src, M.src16) == -1) return -1; + if (__mkntpath(dst, M.dst16) == -1) return -1; + if (CopyFile(M.src16, M.dst16, !!(flags & COPYFILE_NOCLOBBER))) { if (flags & COPYFILE_PRESERVE_TIMESTAMPS) { - fhsrc = CreateFile(src16, kNtFileReadAttributes, kNtFileShareRead, NULL, - kNtOpenExisting, kNtFileAttributeNormal, 0); - fhdst = CreateFile(dst16, kNtFileWriteAttributes, kNtFileShareRead, NULL, + fhsrc = CreateFile(M.src16, kNtFileReadAttributes, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0); + fhdst = CreateFile(M.dst16, kNtFileWriteAttributes, kNtFileShareRead, + NULL, kNtOpenExisting, kNtFileAttributeNormal, 0); if (fhsrc != -1 && fhdst != -1) { GetFileTime(fhsrc, NULL, &accessed, &modified); SetFileTime(fhdst, NULL, &accessed, &modified); diff --git a/libc/calls/createpipename.c b/libc/calls/createpipename.c index a2bf78bc8..bbbddb4ed 100644 --- a/libc/calls/createpipename.c +++ b/libc/calls/createpipename.c @@ -20,7 +20,7 @@ #include "libc/intrin/atomic.h" #include "libc/runtime/internal.h" -static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) { +static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) { char t; size_t a, b, i = 0; do { @@ -38,7 +38,7 @@ static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) { } // This function is called very early by WinMain(). -dontasan textwindows char16_t *__create_pipe_name(char16_t *a) { +textwindows char16_t *__create_pipe_name(char16_t *a) { char16_t *p = a; const char *q = "\\\\?\\pipe\\cosmo\\"; static atomic_uint x; diff --git a/libc/calls/dtime.c b/libc/calls/dtime.c deleted file mode 100644 index ac8465c84..000000000 --- a/libc/calls/dtime.c +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/struct/timespec.h" - -// TODO(jart): DELETE - -/** - * Returns seconds since epoch w/ high-precision. - * @param clockid can be CLOCK_{REALTIME,MONOTONIC}, etc. - */ -long double dtime(int clockid) { - long double secs; - struct timespec tv; - clock_gettime(clockid, &tv); - secs = tv.tv_nsec; - secs *= 1e-9; - secs += tv.tv_sec; - return secs; -} diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 23887098f..e81c2010b 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -55,6 +55,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } if (g_fds.p[newfd].kind) { sys_close_nt(g_fds.p + newfd, newfd); + bzero(g_fds.p + newfd, sizeof(*g_fds.p)); } } diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index e9d928cc4..15e710773 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -167,8 +167,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd, } bool32 ok; - struct NtOverlapped ov = {.hEvent = f->handle, - .Pointer = (void *)(uintptr_t)off}; + struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = off}; if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) { @@ -254,8 +253,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd, // allow a big range to unlock many small ranges for (flp = &g_locks.list, fl = *flp; fl;) { if (fl->fd == fd && EncompassesFileLock(fl, off, len)) { - struct NtOverlapped ov = {.hEvent = f->handle, - .Pointer = (void *)(uintptr_t)fl->off}; + struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off}; if (UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) { *flp = fl->next; ft = fl->next; @@ -287,14 +285,13 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd, off + len >= fl->off && // off + len < fl->off + fl->len) { // cleave left side of lock - struct NtOverlapped ov = {.hEvent = f->handle, - .Pointer = (void *)(uintptr_t)fl->off}; + struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off}; if (!UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) { return -1; } fl->len = (fl->off + fl->len) - (off + len); fl->off = off + len; - ov.Pointer = (void *)(uintptr_t)fl->off; + ov.Pointer = fl->off; if (!LockFileEx(f->handle, kNtLockfileExclusiveLock, 0, fl->len, fl->len >> 32, &ov)) { return -1; diff --git a/libc/calls/fdexists.c b/libc/calls/fdexists.c new file mode 100644 index 000000000..34b0f32ea --- /dev/null +++ b/libc/calls/fdexists.c @@ -0,0 +1,51 @@ +/*-*- 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/calls/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/sysv/consts/f.h" + +/** + * Returns true if file descriptor exists. + * + * This function is equivalent to: + * + * fcntl(fd, F_GETFL) != -1 + * + * Except it won't clobber `errno` and has minimal linkage. + */ +bool32 fdexists(int fd) { + bool32 res; + if (IsWindows() || IsMetal()) { + int e = errno; + if (__sys_fcntl(fd, F_GETFL) != -1) { + res = true; + } else { + errno = e; + res = false; + } + } else { + res = __isfdopen(fd); + } + STRACE("fdexists(%d) → %hhhd", fd, res); + return res; +} diff --git a/libc/calls/fexecve.c b/libc/calls/fexecve.c index d80aba034..c7c898123 100644 --- a/libc/calls/fexecve.c +++ b/libc/calls/fexecve.c @@ -21,7 +21,7 @@ #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" -#include "libc/calls/execve.internal.h" +#include "libc/proc/execve.internal.h" #include "libc/calls/internal.h" #include "libc/calls/struct/stat.internal.h" #include "libc/calls/syscall-sysv.internal.h" diff --git a/libc/calls/foist.c b/libc/calls/foist.c deleted file mode 100644 index 7626d66d9..000000000 --- a/libc/calls/foist.c +++ /dev/null @@ -1,115 +0,0 @@ -/*-*- 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/sig.internal.h" -#include "libc/calls/struct/ucontext.internal.h" -#include "libc/calls/ucontext.h" -#include "libc/dce.h" -#include "libc/fmt/itoa.h" -#include "libc/intrin/atomic.h" -#include "libc/macros.internal.h" -#include "libc/nt/enum/context.h" -#include "libc/nt/enum/ctrlevent.h" -#include "libc/nt/enum/threadaccess.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/context.h" -#include "libc/nt/thread.h" -#include "libc/nt/thunk/msabi.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/tls2.internal.h" - -#ifdef __x86_64__ - -// WIN32 doesn't have the System V red-zone. Microsoft says they reserve -// the right to trample all over it. so we technically don't need to use -// this value. it's just not clear how common it is for WIN32 to clobber -// the red zone, which means broken code could seem to mostly work which -// means it's better that we're not the ones responsible for breaking it -#define kRedzoneSize 128 - -// Both Microsoft and the Fifth Bell System agree on this one. -#define kStackAlign 16 - -__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; -__msabi extern typeof(GetLastError) *const __imp_GetLastError; -__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; -__msabi extern typeof(GetThreadContext) *const __imp_GetThreadContext; -__msabi extern typeof(OpenThread) *const __imp_OpenThread; -__msabi extern typeof(ResumeThread) *const __imp_ResumeThread; -__msabi extern typeof(SetThreadContext) *const __imp_SetThreadContext; -__msabi extern typeof(SuspendThread) *const __imp_SuspendThread; -__msabi extern typeof(WriteFile) *const __imp_WriteFile; - -int WinThreadLaunch(struct Delivery *, long, int (*)(struct Delivery *), long); - -static textwindows unsigned long StrLen(const char *s) { - unsigned long n = 0; - while (*s++) ++n; - return n; -} - -static textwindows void Log(const char *s) { -#if IsModeDbg() - __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); -#endif -} - -/** - * Executes signal handler asynchronously inside other thread. - * - * @return 0 on success, or -1 on error - */ -textwindows int _pthread_signal(struct PosixThread *pt, int sig, int sic) { - int rc = -1; - intptr_t th; - if ((th = __imp_OpenThread( - kNtThreadSuspendResume | kNtThreadGetContext, false, - atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)))) { - uint32_t old_suspend_count; - if ((old_suspend_count = __imp_SuspendThread(th)) != -1u) { - if (!old_suspend_count && - atomic_load_explicit(&pt->status, memory_order_acquire) < - kPosixThreadTerminated) { - struct NtContext nc = {.ContextFlags = kNtContextAll}; - struct Delivery pkg = {0, sig, sic, &nc}; - if (__imp_GetThreadContext(th, &nc)) { - struct CosmoTib *mytls; - mytls = __get_tls(); - __set_tls_win32(pt->tib); - rc = WinThreadLaunch( - &pkg, 0, __sig_tramp, - ROUNDDOWN(nc.Rsp - kRedzoneSize, kStackAlign) - 8); - __imp_SetThreadContext(th, &nc); - __set_tls_win32(mytls); - } else { - Log("GetThreadContext failed\n"); - } - } - __imp_ResumeThread(th); - } else { - Log("SuspendThread failed\n"); - } - __imp_CloseHandle(th); - } else { - Log("OpenThread failed\n"); - } - return rc; -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 654a92311..f403a5cb1 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -122,7 +122,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) { } else { st.st_ctim = st.st_mtim; } - st.st_gid = st.st_uid = __synthesize_uid(); + st.st_gid = st.st_uid = sys_getuid_nt(); st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow; st.st_dev = wst.dwVolumeSerialNumber; st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow; diff --git a/libc/calls/fstatfs.c b/libc/calls/fstatfs.c index 8d7fb2dae..f29def020 100644 --- a/libc/calls/fstatfs.c +++ b/libc/calls/fstatfs.c @@ -35,10 +35,13 @@ * @cancellationpoint */ int fstatfs(int fd, struct statfs *sf) { - int rc; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" union statfs_meta m; - BEGIN_CANCELLATION_POINT; CheckLargeStackAllocation(&m, sizeof(m)); +#pragma GCC pop_options + int rc; + BEGIN_CANCELLATION_POINT; if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { rc = enotsup(); diff --git a/libc/calls/getdtablesize.c b/libc/calls/getdtablesize.c index 79381bfd7..028bb6b64 100644 --- a/libc/calls/getdtablesize.c +++ b/libc/calls/getdtablesize.c @@ -16,14 +16,17 @@ │ 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/struct/rlimit.h" +#include "libc/limits.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/rlimit.h" /** - * Gets file descriptor table size. - * - * @return current limit on the number of open files per process + * Returns limit on number of open files. */ int getdtablesize(void) { - return g_fds.n; + struct rlimit rl = {10000}; + getrlimit(RLIMIT_NOFILE, &rl); + return MIN(rl.rlim_cur, INT_MAX); } diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index e82faf005..d2c987596 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -16,12 +16,14 @@ │ 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/metalfile.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/cosmo.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/bits.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/runtime.h" @@ -30,124 +32,113 @@ #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/ok.h" -#define SIZE 1024 #define CTL_KERN 1 #define KERN_PROC 14 #define KERN_PROC_PATHNAME_FREEBSD 12 #define KERN_PROC_PATHNAME_NETBSD 5 -static struct ProgramExecutableName { - _Atomic(uint32_t) once; - char buf[PATH_MAX]; -} program_executable_name; +static struct { + atomic_uint once; + union { + char buf[PATH_MAX]; + char16_t buf16[PATH_MAX / 2]; + } u; +} g_prog; static inline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } -static inline char *StrCat(char buf[PATH_MAX], const char *a, const char *b) { - char *p, *e; - p = buf; - e = buf + PATH_MAX; - while (*a && p < e) *p++ = *a++; - while (*b && p < e) *p++ = *b++; - return buf; -} - -static inline void GetProgramExecutableNameImpl(char *p, char *e) { - int c; - char *q; - char **ep; - ssize_t rc; - size_t i, n; - union { - int cmd[4]; - char path[PATH_MAX]; - char16_t path16[PATH_MAX]; - } u; +static inline void InitProgramExecutableNameImpl(void) { if (IsWindows()) { - n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16)); - for (i = 0; i < n; ++i) { + int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16)); + for (int i = 0; i < n; ++i) { // turn c:\foo\bar into c:/foo/bar - if (u.path16[i] == '\\') { - u.path16[i] = '/'; + if (g_prog.u.buf16[i] == '\\') { + g_prog.u.buf16[i] = '/'; } } - if (IsAlpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { + if (IsAlpha(g_prog.u.buf16[0]) && // + g_prog.u.buf16[1] == ':' && // + g_prog.u.buf16[2] == '/') { // turn c:/... into /c/... - u.path16[1] = u.path16[0]; - u.path16[0] = '/'; - u.path16[2] = '/'; + g_prog.u.buf16[1] = g_prog.u.buf16[0]; + g_prog.u.buf16[0] = '/'; + g_prog.u.buf16[2] = '/'; } - tprecode16to8(p, e - p, u.path16); + tprecode16to8(g_prog.u.buf, sizeof(g_prog.u.buf), g_prog.u.buf16); return; } + char c, *q; if (IsMetal()) { - if (!memccpy(p, APE_COM_NAME, 0, e - p - 1)) { - e[-1] = 0; - } - return; + q = APE_COM_NAME; + goto CopyString; } // if argv[0] exists then turn it into an absolute path. we also try // adding a .com suffix since the ape auto-appends it when resolving - if ((q = __argv[0]) && ((!sys_faccessat(AT_FDCWD, q, F_OK, 0)) || - ((q = StrCat(u.path, __argv[0], ".com")) && - !sys_faccessat(AT_FDCWD, q, F_OK, 0)))) { + if ((q = __argv[0])) { + char *p = g_prog.u.buf; + char *e = p + sizeof(g_prog.u.buf); if (*q != '/') { if (q[0] == '.' && q[1] == '/') { q += 2; } - if (getcwd(p, e - p)) { + if (getcwd(p, e - p - 1 - 4)) { // for / and .com while (*p) ++p; *p++ = '/'; } } - for (i = 0; *q && p + 1 < e; ++p, ++q) { - *p = *q; + while ((c = *q++)) { + if (p + 1 + 4 < e) { // for nul and .com + *p++ = c; + } } *p = 0; - return; + if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return; + p = WRITE32LE(p, READ32LE(".com")); + *p = 0; + if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return; } // if getenv("_") exists then use that - for (ep = __envp; (q = *ep); ++ep) { + for (char **ep = __envp; (q = *ep); ++ep) { if (*q++ == '_' && *q++ == '=') { - while ((c = *q++)) { - if (p + 1 < e) { - *p++ = c; - } - } - *p = 0; - return; + goto CopyString; } } // if argv[0] doesn't exist, then fallback to interpreter name - if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 || - (rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) { - p[rc] = 0; + ssize_t got; + char *b = g_prog.u.buf; + size_t n = sizeof(g_prog.u.buf) - 1; + if ((got = sys_readlinkat(AT_FDCWD, "/proc/self/exe", b, n)) > 0 || + (got = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", b, n)) > 0) { + b[got] = 0; return; } if (IsFreebsd() || IsNetbsd()) { - u.cmd[0] = CTL_KERN; - u.cmd[1] = KERN_PROC; + int cmd[4]; + cmd[0] = CTL_KERN; + cmd[1] = KERN_PROC; if (IsFreebsd()) { - u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD; + cmd[2] = KERN_PROC_PATHNAME_FREEBSD; } else { - u.cmd[2] = KERN_PROC_PATHNAME_NETBSD; + cmd[2] = KERN_PROC_PATHNAME_NETBSD; } - u.cmd[3] = -1; // current process - n = e - p; - if (sys_sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) { + cmd[3] = -1; // current process + if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) { return; } } - // otherwise give up and just copy argv[0] into it - if (!*p && (q = __argv[0])) { + // give up and just copy argv[0] into it + if ((q = __argv[0])) { + CopyString: + char *p = g_prog.u.buf; + char *e = p + sizeof(g_prog.u.buf); while ((c = *q++)) { if (p + 1 < e) { *p++ = c; @@ -155,14 +146,14 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { } *p = 0; } + + // if we don't even have that then empty the string + g_prog.u.buf[0] = 0; } static void InitProgramExecutableName(void) { - int e; - e = errno; - GetProgramExecutableNameImpl( - program_executable_name.buf, - program_executable_name.buf + sizeof(program_executable_name.buf)); + int e = errno; + InitProgramExecutableNameImpl(); errno = e; } @@ -170,6 +161,6 @@ static void InitProgramExecutableName(void) { * Returns absolute path of program. */ char *GetProgramExecutableName(void) { - cosmo_once(&program_executable_name.once, InitProgramExecutableName); - return program_executable_name.buf; + cosmo_once(&g_prog.once, InitProgramExecutableName); + return g_prog.u.buf; } diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 47ba7fe1e..563a79b96 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -40,7 +40,7 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) { if (!usage) return 0; me = GetCurrentProcess(); if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( - (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), + who == RUSAGE_SELF ? GetCurrentProcess() : GetCurrentThread(), &ftCreation, &ftExit, &ftKernel, &ftUser) || !GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) || !GetProcessIoCounters(me, &iocount)) { diff --git a/libc/calls/gettimeofday-m1.c b/libc/calls/gettimeofday-m1.c index 5fb66cfc4..68f1d9122 100644 --- a/libc/calls/gettimeofday-m1.c +++ b/libc/calls/gettimeofday-m1.c @@ -26,7 +26,7 @@ axdx_t sys_gettimeofday_m1(struct timeval *tv, struct timezone *tz, void *wut) { axdx_t ad; struct timespec ts; - ad.ax = _sysret(__syslib->clock_gettime(CLOCK_REALTIME, &ts)); + ad.ax = _sysret(__syslib->__clock_gettime(CLOCK_REALTIME, &ts)); ad.dx = 0; if (!ad.ax && tv) { *tv = timespec_totimeval(ts); diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 8943f77fe..12f932d1e 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -49,21 +49,15 @@ static gettimeofday_f *__gettimeofday = __gettimeofday_init; * * @param tv points to timeval that receives result if non-NULL * @param tz receives UTC timezone if non-NULL - * @error EFAULT if `tv` or `tz` isn't valid memory * @see clock_gettime() for nanosecond precision * @see strftime() for string formatting */ int gettimeofday(struct timeval *tv, struct timezone *tz) { - int rc; - if (IsAsan() && ((tv && !__asan_is_valid(tv, sizeof(*tv))) || - (tz && !__asan_is_valid(tz, sizeof(*tz))))) { - rc = efault(); - } else { - rc = __gettimeofday(tv, tz, 0).ax; - } + int rc = __gettimeofday(tv, tz, 0).ax; #if SYSDEBUG if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) { - STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc); + POLLTRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, + rc); } #endif return rc; diff --git a/libc/calls/synthuid.c b/libc/calls/getuid-nt.c similarity index 95% rename from libc/calls/synthuid.c rename to libc/calls/getuid-nt.c index d8462b144..34c88e768 100644 --- a/libc/calls/synthuid.c +++ b/libc/calls/getuid-nt.c @@ -22,6 +22,8 @@ #include "libc/macros.internal.h" #include "libc/nt/accounting.h" +// asan must be disabled because __proc_worker calls this on win32 stack + static uint32_t __kmp32(const void *buf, size_t size) { size_t i; uint32_t h; @@ -31,7 +33,7 @@ static uint32_t __kmp32(const void *buf, size_t size) { return h; } -textwindows uint32_t __synthesize_uid(void) { +textwindows uint32_t sys_getuid_nt(void) { char16_t buf[257]; static atomic_uint uid; uint32_t tmp, size = ARRAYLEN(buf); diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index c90336a47..e63eebd25 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -47,7 +47,7 @@ uint32_t getuid(void) { } else if (!IsWindows()) { rc = sys_getuid(); } else { - rc = __synthesize_uid(); + rc = sys_getuid_nt(); } npassert(rc >= 0); STRACE("%s() → %d", "getuid", rc); @@ -72,7 +72,7 @@ uint32_t getgid(void) { } else if (!IsWindows()) { rc = sys_getgid(); } else { - rc = __synthesize_uid(); + rc = sys_getuid_nt(); } npassert(rc >= 0); STRACE("%s() → %d", "getgid", rc); diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 0b8af5aea..5d9651c46 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -6,11 +6,8 @@ #include "libc/dce.h" #include "libc/macros.internal.h" -#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */ - +#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */ #define kSigOpRestartable 1 -#define kSigOpNochld 2 -#define kSigOpUnmaskable 4 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -27,7 +24,7 @@ void __releasefd(int); int __ensurefds(int); int __ensurefds_unlocked(int); void __printfds(void); -uint32_t __synthesize_uid(void); +uint32_t sys_getuid_nt(void); forceinline int64_t __getfdhandleactual(int fd) { return g_fds.p[fd].handle; diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index ade78806d..ae89b7123 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -18,35 +18,28 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/weaken.h" -#include "libc/nt/struct/windowplacement.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" textwindows int _check_interrupts(int sigops) { - int e, rc, flags = 0; - e = errno; + int status; + errno_t err; + struct PosixThread *pt = _pthread_self(); if (_weaken(pthread_testcancel_np) && - (rc = _weaken(pthread_testcancel_np)())) { - errno = rc; + (err = _weaken(pthread_testcancel_np)())) { + goto Interrupted; + } + if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) { + if (status == 2 && (sigops & kSigOpRestartable)) { + return 0; + } + err = EINTR; + Interrupted: + pt->abort_errno = errno = err; return -1; } - if (__tls_enabled) { - flags = __get_tls()->tib_flags; - __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL; - } - if (_weaken(_check_sigalrm)) { - _weaken(_check_sigalrm)(); - } - if (__tls_enabled) { - __get_tls()->tib_flags = flags; - } - if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) { - return eintr(); - } - errno = e; return 0; } diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 7592a093c..1de96fe88 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -40,6 +40,7 @@ #include "libc/nt/struct/ipadapteraddresses.h" #include "libc/nt/winsock.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/sock/internal.h" #include "libc/sock/struct/ifconf.h" #include "libc/sock/struct/ifreq.h" @@ -485,8 +486,12 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) { if (IsLinux()) { return sys_ioctl(fd, SIOCGIFCONF, ifc); } +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" bufMax = 15000; /* conservative guesstimate */ b = alloca(bufMax); + CheckLargeStackAllocation(b, bufMax); +#pragma GCC pop_options memcpy(ifcBsd, &bufMax, 8); /* ifc_len */ memcpy(ifcBsd + (IsXnu() ? 4 : 8), &b, 8); /* ifc_buf */ if ((rc = sys_ioctl(fd, SIOCGIFCONF, &ifcBsd)) != -1) { diff --git a/libc/calls/isexecutable.c b/libc/calls/isexecutable.c index caa74b45e..f2b51c335 100644 --- a/libc/calls/isexecutable.c +++ b/libc/calls/isexecutable.c @@ -29,7 +29,7 @@ * @vforksafe */ bool32 isexecutable(const char *path) { - struct stat st; /* execve() depends on this */ + struct stat st; if (fstatat(AT_FDCWD, path, &st, 0)) return 0; return !S_ISDIR(st.st_mode) && !!(st.st_mode & 0111); } diff --git a/libc/calls/kill-nt.c b/libc/calls/kill-nt.c deleted file mode 100644 index afbc4c2cc..000000000 --- a/libc/calls/kill-nt.c +++ /dev/null @@ -1,123 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/dce.h" -#include "libc/macros.internal.h" -#include "libc/nt/console.h" -#include "libc/nt/enum/ctrlevent.h" -#include "libc/nt/enum/processaccess.h" -#include "libc/nt/enum/th32cs.h" -#include "libc/nt/errors.h" -#include "libc/nt/process.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/processentry32.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/errfuns.h" - -static inline int GetConsoleCtrlEvent(int sig) { - switch (sig) { - case SIGINT: - return kNtCtrlCEvent; - case SIGQUIT: - return kNtCtrlBreakEvent; - default: - return -1; - } -} - -textwindows int sys_kill_nt(int pid, int sig) { - bool32 ok; - int64_t h; - int event, ntpid; - - // is killing everything except init really worth supporting? - if (pid == -1) return einval(); - - // XXX: NT doesn't really have process groups. For instance the - // CreateProcess() flag for starting a process group actually - // just does an "ignore ctrl-c" internally. - pid = ABS(pid); - - // If we're targeting current process group then just call raise(). - if (!pid || pid == getpid()) { - if (!sig) return 0; // ability check passes - return raise(sig); - } - - // GenerateConsoleCtrlEvent() will always signal groups and there's - // nothing we can do about it, unless we have a GUI GetMessage loop - // and alternatively create a centralized signal daemon like cygwin - if ((event = GetConsoleCtrlEvent(sig)) != -1) { - // we're killing with SIGINT or SIGQUIT which are the only two - // signals we can really use, since TerminateProcess() makes - // everything else effectively a SIGKILL ;_; - if (__isfdkind(pid, kFdProcess)) { - ntpid = GetProcessId(g_fds.p[pid].handle); - } else if (!__isfdopen(pid)) { - ntpid = pid; // XXX: suboptimal - } else { - return esrch(); - } - if (GenerateConsoleCtrlEvent(event, ntpid)) { - return 0; - } else { - return -1; - } - } - - // is this a cosmo pid that was returned by fork? - if (__isfdkind(pid, kFdProcess)) { - if (!sig) return 0; // ability check passes - // since windows can't execve we need to kill the grandchildren - // TODO(jart): should we just kill the whole tree too? there's - // no obvious way to tell if it's the execve shell - struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)}; - ntpid = GetProcessId(g_fds.p[pid].handle); - int64_t hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0); - if (Process32First(hSnap, &pe)) { - do { - if (pe.th32ParentProcessID == ntpid) { - if ((h = OpenProcess(kNtProcessTerminate, false, pe.th32ProcessID))) { - TerminateProcess(h, sig); - CloseHandle(h); - } - } - } while (Process32Next(hSnap, &pe)); - } - CloseHandle(hSnap); - ok = TerminateProcess(g_fds.p[pid].handle, sig); - if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true; - return 0; - } - - // XXX: Is this a raw new technology pid? Because that's messy. - if ((h = OpenProcess(kNtProcessTerminate, false, pid))) { - if (sig) { - ok = TerminateProcess(h, sig); - if (!ok && GetLastError() == kNtErrorAccessDenied) { - ok = true; // cargo culting other codebases here - } - } - CloseHandle(h); - return 0; - } else { - return -1; - } -} diff --git a/libc/calls/linkat-nt.c b/libc/calls/linkat-nt.c index 94d6efae1..d7063b7ab 100644 --- a/libc/calls/linkat-nt.c +++ b/libc/calls/linkat-nt.c @@ -20,17 +20,24 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/runtime/stack.h" textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - char16_t newpath16[PATH_MAX]; - char16_t oldpath16[PATH_MAX]; - if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) != -1 && - __mkntpathat(newdirfd, newpath, 0, newpath16) != -1) { - if (CreateHardLink(newpath16, oldpath16, NULL)) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + struct { + char16_t newpath16[PATH_MAX]; + char16_t oldpath16[PATH_MAX]; + } M; + CheckLargeStackAllocation(&M, sizeof(M)); +#pragma GCC pop_options + if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) != -1 && + __mkntpathat(newdirfd, newpath, 0, M.newpath16) != -1) { + if (CreateHardLink(M.newpath16, M.oldpath16, NULL)) { return 0; } else { - return __fix_enotdir3(__winerr(), newpath16, oldpath16); + return __fix_enotdir3(__winerr(), M.newpath16, M.oldpath16); } } else { return -1; diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index 089c61c29..0c13a5b0e 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -16,21 +16,63 @@ │ 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/syscall_support-nt.internal.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" +#include "libc/nt/struct/byhandlefileinformation.h" +#include "libc/stdckdint.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" -textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { - int64_t res; - if (__isfdkind(fd, kFdFile)) { - if (GetFileType(g_fds.p[fd].handle) != kNtFileTypePipe) { - if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) { - return res; - } else { +static textwindows int64_t GetPosition(struct Fd *f, int whence) { + switch (whence) { + case SEEK_SET: + return 0; + case SEEK_CUR: + return f->pointer; + case SEEK_END: { + struct NtByHandleFileInformation wst; + if (!GetFileInformationByHandle(f->handle, &wst)) { return __winerr(); } + return (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow; + } + default: + return einval(); + } +} + +static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) { + int64_t pos; + if ((pos = GetPosition(f, whence)) != -1) { + if (!ckd_add(&pos, pos, offset)) { + if (pos >= 0) { + return pos; + } else { + return einval(); + } + } else { + return eoverflow(); + } + } else { + return -1; + } +} + +textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { + if (__isfdkind(fd, kFdFile)) { + struct Fd *f = g_fds.p + fd; + int filetype = GetFileType(f->handle); + if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) { + int64_t res; + pthread_mutex_lock(&f->lock); + if ((res = Seek(f, offset, whence)) != -1) { + f->pointer = res; + } + pthread_mutex_unlock(&f->lock); + return res; } else { return espipe(); } diff --git a/libc/calls/metalfile.c b/libc/calls/metalfile.c index 3b46de96e..271d333d2 100644 --- a/libc/calls/metalfile.c +++ b/libc/calls/metalfile.c @@ -52,7 +52,7 @@ __static_yoink("_init_metalfile"); void *__ape_com_base; size_t __ape_com_size = 0; -textstartup dontasan void InitializeMetalFile(void) { +textstartup void InitializeMetalFile(void) { if (IsMetal()) { /* * Copy out a pristine image of the program — before the program might diff --git a/libc/calls/mkdtemp.c b/libc/calls/mkdtemp.c index 59fe2e9ae..708732de4 100644 --- a/libc/calls/mkdtemp.c +++ b/libc/calls/mkdtemp.c @@ -35,6 +35,7 @@ * with random text on success (and not modified on error) * @return pointer to template on success, or NULL w/ errno * @raise EINVAL if template didn't end with XXXXXX + * @cancellationpoint */ char *mkdtemp(char *template) { int n; diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 5081c4835..b29ccc1d0 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/mem/mem.h" #include "libc/str/str.h" #include "libc/str/thompike.h" diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index 7a69acc5f..6d936b61e 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/fmt/conv.h" #include "libc/intrin/bits.h" #include "libc/intrin/getenv.internal.h" @@ -25,6 +25,7 @@ #include "libc/mem/arraylist2.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/str/thompike.h" #include "libc/str/utf16.h" @@ -141,7 +142,12 @@ textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[], bool have_systemroot = false; size_t i, j, k, n, m, bufi = 0; for (n = 0; envp[n];) n++; - vars = alloca((n + 1) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (n + 1) * sizeof(char *); + vars = alloca(nbytes); + CheckLargeStackAllocation(vars, nbytes); +#pragma GCC pop_options for (i = 0; i < n; ++i) { InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot); } diff --git a/libc/calls/mkostemp.c b/libc/calls/mkostemp.c index b0a8ff4cb..bb9fea9ca 100644 --- a/libc/calls/mkostemp.c +++ b/libc/calls/mkostemp.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/temp.h" #include "libc/sysv/consts/at.h" +#include "libc/temp.h" /** * Creates temporary file name and file descriptor, e.g. @@ -34,6 +34,7 @@ * @see mkstemp() if you don't need flags * @see mktemp() if you don't need an fd * @see tmpfd() if you don't need a path + * @cancellationpoint */ int mkostemp(char *template, unsigned flags) { return openatemp(AT_FDCWD, template, 0, flags, 0); diff --git a/libc/calls/mkostemps.c b/libc/calls/mkostemps.c index 417b7af6a..13f9b81d5 100644 --- a/libc/calls/mkostemps.c +++ b/libc/calls/mkostemps.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/temp.h" #include "libc/sysv/consts/at.h" +#include "libc/temp.h" /** * Creates temporary file name and file descriptor, e.g. @@ -35,6 +35,7 @@ * @see mkostemp() if you don't need suffix * @see mktemp() if you don't need an fd * @see tmpfd() if you don't need a path + * @cancellationpoint */ int mkostemps(char *template, int suffixlen, unsigned flags) { return openatemp(AT_FDCWD, template, suffixlen, flags, 0); diff --git a/libc/calls/mkstemp.c b/libc/calls/mkstemp.c index ad71bcdf4..2d9dd277f 100644 --- a/libc/calls/mkstemp.c +++ b/libc/calls/mkstemp.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/temp.h" #include "libc/sysv/consts/at.h" +#include "libc/temp.h" /** * Creates temporary file name and file descriptor, e.g. @@ -34,6 +34,7 @@ * @see mkstemps() if you you need a suffix * @see mktemp() if you don't need an fd * @see tmpfd() if you don't need a path + * @cancellationpoint */ int mkstemp(char *template) { return openatemp(AT_FDCWD, template, 0, 0, 0); diff --git a/libc/calls/mkstemps.c b/libc/calls/mkstemps.c index c69dc2f1f..0663b9e0d 100644 --- a/libc/calls/mkstemps.c +++ b/libc/calls/mkstemps.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/temp.h" #include "libc/sysv/consts/at.h" +#include "libc/temp.h" /** * Creates temporary file name and file descriptor, e.g. @@ -36,6 +36,7 @@ * @see mkstemp() if you don't need `suffixlen` * @see mktemp() if you don't need an fd * @see tmpfd() if you don't need a path + * @cancellationpoint */ int mkstemps(char *template, int suffixlen) { return openatemp(AT_FDCWD, template, suffixlen, 0, 0); diff --git a/libc/calls/mktemp.c b/libc/calls/mktemp.c index d4a4ea4c4..568da1feb 100644 --- a/libc/calls/mktemp.c +++ b/libc/calls/mktemp.c @@ -36,6 +36,7 @@ * @see mkstemps() if you you need a file extension * @see openatemp() for one temp roller to rule them all * @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc. + * @cancellationpoint */ char *mktemp(char *template) { int fd; diff --git a/libc/calls/munmap-metal.c b/libc/calls/munmap-metal.c index 5bdbdcae8..de8949d6c 100644 --- a/libc/calls/munmap-metal.c +++ b/libc/calls/munmap-metal.c @@ -20,7 +20,7 @@ #include "libc/runtime/pc.internal.h" #ifdef __x86_64__ -dontasan int sys_munmap_metal(void *addr, size_t size) { +int sys_munmap_metal(void *addr, size_t size) { size_t i; uint64_t *e, paddr; struct mman *mm = __get_mm(); diff --git a/libc/calls/nanosleep-xnu.c b/libc/calls/nanosleep-xnu.c index 9e8056b13..21dad75c5 100644 --- a/libc/calls/nanosleep-xnu.c +++ b/libc/calls/nanosleep-xnu.c @@ -46,6 +46,6 @@ int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) { } return rc; #else - return _sysret(__syslib->nanosleep(req, rem)); + return _sysret(__syslib->__nanosleep(req, rem)); #endif } diff --git a/libc/calls/now.c b/libc/calls/now.c deleted file mode 100644 index a0fba41dc..000000000 --- a/libc/calls/now.c +++ /dev/null @@ -1,117 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/calls/clock_gettime.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/syscall_support-sysv.internal.h" -#include "libc/dce.h" -#include "libc/intrin/bits.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/rdtsc.h" -#include "libc/nexgen32e/x86feature.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/clock.h" -#include "libc/thread/tls.h" -#include "libc/time/time.h" - -// TODO(jart): DELETE - -static clock_gettime_f *__gettime; - -static struct Now { - uint64_t k0; - long double r0, cpn; -} g_now; - -static long double GetTimeSample(void) { - uint64_t tick1, tick2; - long double time1, time2; - sched_yield(); - time1 = dtime(CLOCK_REALTIME_PRECISE); - tick1 = rdtsc(); - nanosleep(&(struct timespec){0, 1000000}, NULL); - time2 = dtime(CLOCK_REALTIME_PRECISE); - tick2 = rdtsc(); - return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); -} - -static long double MeasureNanosPerCycle(void) { - int i, n; - long double avg, samp; - if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL; - if (IsWindows()) { - n = 10; - } else { - n = 5; - } - for (avg = 1.0L, i = 1; i < n; ++i) { - samp = GetTimeSample(); - avg += (samp - avg) / i; - } - if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL; - STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); - return avg; -} - -void RefreshTime(void) { - struct Now now; - now.cpn = MeasureNanosPerCycle(); - now.r0 = dtime(CLOCK_REALTIME_PRECISE); - now.k0 = rdtsc(); - memcpy(&g_now, &now, sizeof(now)); -} - -static long double nowl_sys(void) { - return dtime(CLOCK_REALTIME_PRECISE); -} - -static long double nowl_art(void) { - uint64_t ticks = rdtsc() - g_now.k0; - return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn)); -} - -static long double nowl_vdso(void) { - long double secs; - struct timespec tv; - unassert(__gettime); - __gettime(CLOCK_REALTIME_PRECISE, &tv); - secs = tv.tv_nsec; - secs *= 1 / 1e9L; - secs += tv.tv_sec; - return secs; -} - -long double nowl_setup(void) { - bool isfast; - __gettime = __clock_gettime_get(&isfast); - if (isfast) { - nowl = nowl_vdso; - } else if (X86_HAVE(INVTSC)) { - RefreshTime(); - nowl = nowl_art; - } else { - nowl = nowl_sys; - } - return nowl(); -} - -long double (*nowl)(void) = nowl_setup; diff --git a/libc/calls/oldbench.c b/libc/calls/oldbench.c deleted file mode 100644 index 5d38c5dea..000000000 --- a/libc/calls/oldbench.c +++ /dev/null @@ -1,84 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/state.internal.h" -#include "libc/calls/struct/timespec.h" -#include "libc/dce.h" -#include "libc/intrin/bits.h" -#include "libc/intrin/safemacros.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/rdtsc.h" -#include "libc/nexgen32e/x86feature.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/clock.h" -#include "libc/thread/tls.h" -#include "libc/time/time.h" - -// TODO(jart): DELETE - -static struct Now { - bool once; - uint64_t k0; - long double r0, cpn; -} g_now; - -static long double GetTimeSample(void) { - uint64_t tick1, tick2; - long double time1, time2; - sched_yield(); - time1 = dtime(CLOCK_MONOTONIC); - tick1 = rdtsc(); - nanosleep(&(struct timespec){0, 1000000}, NULL); - time2 = dtime(CLOCK_MONOTONIC); - tick2 = rdtsc(); - return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); -} - -static long double MeasureNanosPerCycle(void) { - int i, n; - long double avg, samp; - if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL; - if (IsWindows()) { - n = 30; - } else { - n = 20; - } - for (avg = 1.0L, i = 1; i < n; ++i) { - samp = GetTimeSample(); - avg += (samp - avg) / i; - } - if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL; - STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); - return avg; -} - -static void Refresh(void) { - struct Now now; - now.cpn = MeasureNanosPerCycle(); - now.r0 = dtime(CLOCK_REALTIME); - now.k0 = rdtsc(); - now.once = true; - memcpy(&g_now, &now, sizeof(now)); -} - -long double ConvertTicksToNanos(double ticks) { - if (!g_now.once) Refresh(); - return ticks * g_now.cpn; /* pico scale */ -} diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c deleted file mode 100644 index 4b14089fd..000000000 --- a/libc/calls/onntconsoleevent.c +++ /dev/null @@ -1,114 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/intrin/atomic.h" -#include "libc/nt/enum/ctrlevent.h" -#include "libc/nt/runtime.h" -#include "libc/nt/thunk/msabi.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" - -#ifdef __x86_64__ - -__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; -__msabi extern typeof(WriteFile) *const __imp_WriteFile; - -static textwindows int GetSig(uint32_t dwCtrlType) { - switch (dwCtrlType) { - case kNtCtrlCEvent: - return SIGINT; - case kNtCtrlBreakEvent: - return SIGQUIT; - case kNtCtrlCloseEvent: - case kNtCtrlLogoffEvent: // only received by services - case kNtCtrlShutdownEvent: // only received by services - return SIGHUP; - default: - return SIGSTKFLT; - } -} - -textwindows bool32 __sig_notify(int sig, int sic) { - - // we're on a stack that's owned by win32. to make matters worse, - // win32 spawns a totally new thread just to invoke this handler. - if (__sighandrvas[sig] == (uintptr_t)SIG_IGN) { - return true; - } - - // if we don't have tls, then we can't hijack a safe stack from a - // thread so just try our luck punting the signal to the next i/o - if (!__tls_enabled) { - __sig.pending |= 1ull << (sig - 1); - return true; - } - - pthread_spin_lock(&_pthread_lock); - - // before we get asynchronous, let's try to find a thread that is - // currently blocked on io which we can interrupt with our signal - // this is important, because if we we asynchronously interrupt a - // thread that's calling ReadFile() by suspending / resuming then - // the io operation will report end of file (why) upon resumation - struct Dll *e; - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { - struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); - int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (tid <= 0) continue; // -1 means spawning, 0 means terminated - if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked - if (pt->flags & PT_BLOCKED) { - pthread_spin_unlock(&_pthread_lock); - __sig.pending |= 1ull << (sig - 1); - return true; - } - } - - // limbo means most of the cosmo runtime is totally broken, which - // means we can't call the user signal handler safely. what we'll - // do instead is pick a posix thread at random to hijack, pretend - // to be that thread, use its stack, and then deliver this signal - // asynchronously if it isn't blocked. hopefully it won't longjmp - // because once the handler returns, we'll restore the old thread - // going asynchronous is heavy but it's the only way to stop code - // that does stuff like scientific computing, which are cpu-bound - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { - struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); - int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (tid <= 0) continue; // -1 means spawning, 0 means terminated - if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked - pthread_spin_unlock(&_pthread_lock); - _pthread_signal(pt, sig, sic); - return true; - } - - pthread_spin_unlock(&_pthread_lock); - return true; -} - -__msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { - __sig_notify(GetSig(dwCtrlType), SI_KERNEL); - return true; -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/onntconsoleevent_init.S b/libc/calls/onntconsoleevent_init.S deleted file mode 100644 index 3e5d06a2f..000000000 --- a/libc/calls/onntconsoleevent_init.S +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/macros.internal.h" - - .init.start 300,_init_onntconsoleevent - testb IsWindows() - jz 1f - ezlea __onntconsoleevent,cx - pushpop 1,%rdx - ntcall __imp_SetConsoleCtrlHandler -1: .init.end 300,_init_onntconsoleevent,globl,hidden diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 70939298c..40ed5551c 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -161,8 +161,9 @@ static textwindows int sys_open_nt_console(int dirfd, } else if ((g_fds.p[fd].handle = sys_open_nt_impl( dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode, kNtFileFlagOverlapped)) != -1) { - g_fds.p[fd].extra = sys_open_nt_impl( - dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode, 0); + g_fds.p[fd].extra = + sys_open_nt_impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, + mode, kNtFileFlagOverlapped); npassert(g_fds.p[fd].extra != -1); } else { return -1; @@ -176,8 +177,8 @@ static textwindows int sys_open_nt_console(int dirfd, static textwindows int sys_open_nt_file(int dirfd, const char *file, uint32_t flags, int32_t mode, size_t fd) { - if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, 0)) != - -1) { + if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, + kNtFileFlagOverlapped)) != -1) { g_fds.p[fd].kind = kFdFile; g_fds.p[fd].flags = flags; g_fds.p[fd].mode = mode; diff --git a/libc/calls/openatemp.c b/libc/calls/openatemp.c index ddb06871e..4697c5370 100644 --- a/libc/calls/openatemp.c +++ b/libc/calls/openatemp.c @@ -86,9 +86,7 @@ * @param template is a pathname relative to current directory by default, * that needs to have "XXXXXX" at the end of the string; this memory * must be mutable and should be owned by the calling thread; it will - * be modified (only on success) to return the generated filename; it - * is recommended that the caller use `kTmpPath` at the beginning of - * the generated `template` path and then set `dirfd` to `AT_FDCWD` + * be modified (only on success) to return the generated filename * @param suffixlen may be nonzero to permit characters after the "XXXXXX" * @param mode is conventionally 0600, for owner-only non-exec access * @param flags could have O_APPEND, O_CLOEXEC, O_UNLINK, O_SYNC, etc. @@ -97,6 +95,7 @@ * @raise EINVAL if `template` (less the `suffixlen` region) didn't * end with the string "XXXXXXX" * @raise EINVAL if `suffixlen` was negative or too large + * @cancellationpoint */ int openatemp(int dirfd, char *template, int suffixlen, int flags, int mode) { flags &= ~O_ACCMODE; diff --git a/libc/calls/pause-nt.c b/libc/calls/pause-nt.c index 8801b5e47..6db3a9292 100644 --- a/libc/calls/pause-nt.c +++ b/libc/calls/pause-nt.c @@ -16,37 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/bo.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" -#include "libc/intrin/strace.internal.h" -#include "libc/nt/errors.h" #include "libc/nt/synchronization.h" -#include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" textwindows int sys_pause_nt(void) { - BEGIN_BLOCKING_OPERATION; - for (;;) { - - if (_check_interrupts(0)) { - return -1; + int rc; + while (!(rc = _check_interrupts(0))) { + struct PosixThread *pt = _pthread_self(); + pt->abort_errno = 0; + pt->pt_flags |= PT_INSEMAPHORE; + WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS); + pt->pt_flags &= ~PT_INSEMAPHORE; + if (pt->abort_errno) { + errno = pt->abort_errno; + rc = -1; + break; } - - if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) { - POLLTRACE("IOCP EINTR"); // in case we ever figure it out - continue; - } - -#if defined(SYSDEBUG) && _POLLTRACE - long ms = 0, totoms = 0; - ms += __SIG_POLLING_INTERVAL_MS; - if (ms >= __SIG_LOGGING_INTERVAL_MS) { - totoms += ms, ms = 0; - POLLTRACE("... pausing for %'lums...", totoms); - } -#endif } - END_BLOCKING_OPERATION; + return rc; } diff --git a/libc/calls/pledge-linux.c b/libc/calls/pledge-linux.c index f098877a7..c8987f639 100644 --- a/libc/calls/pledge-linux.c +++ b/libc/calls/pledge-linux.c @@ -1078,81 +1078,81 @@ static privileged int GetTid(void) { return res; } +static privileged long Write(int fd, const void *p, unsigned long n) { +#ifdef __x86_64__ + long res; + asm volatile("syscall" + : "=a"(res) + : "0"(__NR_linux_write), "D"(2), "S"(p), "d"(n) + : "rcx", "r11", "memory"); + return res; +#elif defined(__aarch64__) + register long x0 asm("x0") = 2; + register long x1 asm("x1") = (long)p; + register long x2 asm("x2") = n; + register long x8 asm("x8") = __NR_linux_write; + asm volatile("svc\t0" : "+r"(x0) : "r"(x1), "r"(x2), "r"(x8) : "memory"); + return x0; +#endif +} + static privileged void Log(const char *s, ...) { va_list va; va_start(va, s); do { -#ifdef __x86_64__ - int res; - asm volatile("syscall" - : "=a"(res) - : "0"(__NR_linux_write), "D"(2), "S"(s), "d"(StrLen(s)) - : "rcx", "r11", "memory"); -#elif defined(__aarch64__) - register long r0 asm("x0") = 2; - register long r1 asm("x1") = (long)s; - register long r2 asm("x2") = StrLen(s); - register long res_x0 asm("x0"); - asm volatile("mov\tx8,%1\n\t" - "svc\t0" - : "=r"(res_x0) - : "i"(__NR_linux_write), "r"(r0), "r"(r1), "r"(r2) - : "x8", "memory"); -#endif + Write(2, s, StrLen(s)); } while ((s = va_arg(va, const char *))); va_end(va); } static privileged int SigAction(int sig, struct sigaction *act, struct sigaction *old) { - int res; act->sa_flags |= Sa_Restorer; act->sa_restorer = &__restore_rt; #ifdef __x86_64__ + int res; asm volatile("mov\t%5,%%r10\n\t" "syscall" : "=a"(res) : "0"(__NR_linux_sigaction), "D"(sig), "S"(act), "d"(old), "g"(8) : "rcx", "r10", "r11", "memory"); -#elif defined(__aarch64__) - register long r0 asm("x0") = (long)sig; - register long r1 asm("x1") = (long)act; - register long r2 asm("x2") = (long)old; - register long r3 asm("x3") = (long)8; - register long res_x0 asm("x0"); - asm volatile("mov\tx8,%1\n\t" - "svc\t0" - : "=r"(res_x0) - : "i"(__NR_linux_sigaction), "r"(r0), "r"(r1), "r"(r2), "r"(r3) - : "x8", "memory"); - res = res_x0; -#endif return res; +#elif defined(__aarch64__) + register int x0 asm("x0") = sig; + register void *x1 asm("x1") = act; + register void *x2 asm("x2") = old; + register int x3 asm("x3") = 8; + register int x8 asm("x8") = __NR_linux_sigaction; + asm volatile("svc\t0" + : "+r"(x0) + : "r"(x1), "r"(x2), "r"(x3), "r"(x8) + : "memory"); + return x0; +#endif } static privileged int SigProcMask(int how, int64_t set, int64_t *old) { - int res; #ifdef __x86_64__ + int res; asm volatile("mov\t%5,%%r10\n\t" "syscall" : "=a"(res) : "0"(__NR_linux_sigprocmask), "D"(how), "S"(&set), "d"(old), "g"(8) : "rcx", "r10", "r11", "memory"); -#elif defined(__aarch64__) - register long r0 asm("x0") = (long)how; - register long r1 asm("x1") = (long)set; - register long r2 asm("x2") = (long)old; - register long r3 asm("x3") = (long)8; - register long res_x0 asm("x0"); - asm volatile("mov\tx8,%1\n\t" - "svc\t0" - : "=r"(res_x0) - : "i"(__NR_linux_sigprocmask), "r"(r0), "r"(r1), "r"(r2), "r"(r3) - : "x8", "memory"); - res = res_x0; -#endif return res; +#elif defined(__aarch64__) + register int x0 asm("x0") = how; + register void *x1 asm("x1") = &set; + register void *x2 asm("x2") = old; + register int x3 asm("x3") = 8; + register long x8 asm("x8") = __NR_linux_sigprocmask; + asm volatile("svc\t0" + : "+r"(x0) + : "r"(x1), "r"(x2), "r"(x3), "r"(x8) + : "memory"); + return x0; +#endif } static privileged void KillThisProcess(void) { @@ -1205,6 +1205,16 @@ static privileged void KillThisThread(void) { : "0"(__NR_linux_tkill), "D"(GetTid()), "S"(Sigabrt) : "rcx", "r11", "memory"); #elif defined(__aarch64__) + { + register long r0 asm("x0") = (long)GetTid(); + register long r1 asm("x1") = (long)Sigabrt; + register long res_x0 asm("x0"); + asm volatile("mov\tx8,%1\n\t" + "svc\t0" + : "=r"(res_x0) + : "i"(__NR_linux_tkill), "r"(r0), "r"(r1) + : "x8", "memory"); + } #endif SigProcMask(Sig_Setmask, 0, 0); #ifdef __x86_64__ @@ -1267,6 +1277,7 @@ static privileged void OnSigSys(int sig, siginfo_t *si, void *vctx) { } switch (mode & PLEDGE_PENALTY_MASK) { case PLEDGE_PENALTY_KILL_PROCESS: + KillThisThread(); KillThisProcess(); // fallthrough case PLEDGE_PENALTY_KILL_THREAD: @@ -2290,10 +2301,13 @@ static privileged void AppendPledge(struct Filter *f, // * @vforksafe */ privileged int sys_pledge_linux(unsigned long ipromises, int mode) { - int i, rc = -1; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" struct Filter f; - struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)}; CheckLargeStackAllocation(&f, sizeof(f)); +#pragma GCC pop_options + struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)}; + int i, rc = -1; f.n = 0; // set up the seccomp filter diff --git a/libc/calls/pledge.c b/libc/calls/pledge.c index 6c24b989d..f5a19f927 100644 --- a/libc/calls/pledge.c +++ b/libc/calls/pledge.c @@ -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/calls/calls.h" #include "libc/calls/pledge.internal.h" #include "libc/calls/prctl.internal.h" @@ -57,15 +58,7 @@ * self-imposed security model. That works best when programs perform * permission-hungry operations (e.g. calling GetSymbolTable) towards * the beginning of execution, and then relinquish privilege afterwards - * by calling pledge(). Here's an example of where that matters. Your - * Cosmopolitan C Library needs to code morph your executable in memory - * once you start using threads. But that's only possible to do if you - * used the `prot_exec` promise. So the right thing to do here, is to - * call __enable_threads() before calling pledge() to force it early. - * - * __enable_threads(); - * ShowCrashReports(); - * pledge("...", 0); + * by calling pledge(). * * By default exit() is allowed. This is useful for processes that * perform pure computation and interface with the parent via shared @@ -260,7 +253,9 @@ int pledge(const char *promises, const char *execpromises) { if (IsGenuineBlink()) return enosys(); if (IsOpenbsd()) return sys_pledge(0, 0); if (!IsLinux()) return enosys(); - if (!(rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0))) return 0; + rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0); + if (rc == 0 || rc == 2) return 0; // 2 means we're already filtered + unassert(rc < 0); errno = -rc; return -1; } else if (!IsTiny() && IsGenuineBlink()) { diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index d05c76d0c..00c0e15ba 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/errno.h" #include "libc/intrin/bits.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" @@ -42,6 +43,7 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" #ifdef __x86_64__ @@ -161,31 +163,31 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, } // if we haven't found any good results yet then here we // compute a small time slice we don't mind sleeping for - waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLLING_INTERVAL_MS, *ms); + waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLL_INTERVAL_MS, *ms); if (sn) { - // we need to poll the socket handles separately because - // microsoft certainly loves to challenge us with coding - // please note that winsock will fail if we pass zero fd #if _NTTRACE POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms); #endif - if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) { + if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) { rc = __winsockerr(); goto ReturnPath; } - *ms -= waitfor; } else { gotsocks = 0; - if (!gotinvals && !gotpipes && waitfor) { - // if we've only got pipes and none of them are ready - // then we'll just explicitly sleep for the time left - POLLTRACE("SleepEx(%'d, false) out of %'lu", waitfor, *ms); - if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) { - POLLTRACE("IOCP EINTR"); - } else { - *ms -= waitfor; - } + } + if (!gotinvals && !gotsocks && !gotpipes && waitfor) { + POLLTRACE("sleeping for %'d out of %'lu ms", waitfor, *ms); + struct PosixThread *pt = _pthread_self(); + pt->abort_errno = 0; + pt->pt_flags |= PT_INSEMAPHORE; + WaitForSingleObject(pt->semaphore, waitfor); + pt->pt_flags &= ~PT_INSEMAPHORE; + if (pt->abort_errno) { + errno = pt->abort_errno; + rc = -1; + goto ReturnPath; } + *ms -= waitfor; // todo: make more resilient } // we gave all the sockets and all the named pipes a shot // if we found anything at all then it's time to end work diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index 8b71e461b..a26fed35b 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -50,7 +50,6 @@ void __printfds(void) { for (i = 0; i < g_fds.n; ++i) { if (!g_fds.p[i].kind) continue; kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind)); - if (g_fds.p[i].zombie) kprintf(" zombie"); if (g_fds.p[i].flags) kprintf(" flags=%#x", g_fds.p[i].flags); if (g_fds.p[i].mode) kprintf(" mode=%#o", g_fds.p[i].mode); if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle); diff --git a/libc/calls/raise.c b/libc/calls/raise.c index 8012c8740..4a3dde47a 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -21,52 +21,33 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" -#include "libc/nt/runtime.h" -#include "libc/runtime/internal.h" +#include "libc/runtime/syslib.internal.h" #include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" #include "libc/thread/tls.h" -#include "libc/thread/xnu.internal.h" - -static dontubsan void RaiseSigFpe(void) { - volatile int x = 0; - x = 1 / x; -} /** * Sends signal to self. * * This is basically the same as: * - * tkill(gettid(), sig); + * pthread_kill(pthread_self(), sig); * * Note `SIG_DFL` still results in process death for most signals. * - * This function is not entirely equivalent to kill() or tkill(). For - * example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that - * helps us support Windows. So if the raised signal has a signal - * handler, then the reported `si_code` might not be `SI_TKILL`. - * * @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc. - * @return 0 if signal was delivered and returned, or -1 w/ errno + * @return 0 on success, or nonzero on failure * @asyncsignalsafe */ int raise(int sig) { int rc; - STRACE("raise(%G) → ...", sig); - if (sig == SIGTRAP) { - DebugBreak(); + if (IsXnuSilicon()) { + rc = __syslib->__raise(sig); + } else if (IsWindows()) { + __sig_raise(sig, SI_TKILL); rc = 0; -#ifndef __aarch64__ - } else if (sig == SIGFPE) { - // TODO(jart): Why doesn't AARCH64 raise SIGFPE? - RaiseSigFpe(); - rc = 0; -#endif } else { - rc = tkill(gettid(), sig); + rc = sys_tkill(gettid(), sig, 0); } - STRACE("...raise(%G) → %d% m", sig, rc); + STRACE("raise(%G) → %d% m", sig, rc); return rc; } diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index f64735651..f8e36f67b 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -16,8 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/bo.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" @@ -49,6 +47,8 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" __static_yoink("WinMainStdin"); @@ -56,12 +56,6 @@ __static_yoink("WinMainStdin"); __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; -static textwindows void sys_read_nt_abort(int64_t handle, - struct NtOverlapped *overlapped) { - unassert(CancelIoEx(handle, overlapped) || - GetLastError() == kNtErrorNotFound); -} - textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size, int64_t offset) { @@ -69,20 +63,37 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size, bool32 ok; struct Fd *f; uint32_t got; - int filetype; int64_t handle; uint32_t remain; char *targetdata; uint32_t targetsize; - bool is_console_input; - int abort_errno = EAGAIN; + struct PosixThread *pt; + f = g_fds.p + fd; + handle = __resolve_stdin_handle(f->handle); + pt = _pthread_self(); + + bool pwriting = offset != -1; + bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk; + bool nonblock = !!(f->flags & O_NONBLOCK); + pt->abort_errno = EAGAIN; + + if (pwriting && !seekable) { + return espipe(); + } + if (!pwriting) { + offset = 0; + } + + uint32_t dwConsoleMode; + bool is_console_input = + g_fds.stdin.handle + ? f->handle == g_fds.stdin.handle + : !seekable && (f->kind == kFdConsole || + GetConsoleMode(handle, &dwConsoleMode)); + StartOver: size = MIN(size, 0x7ffff000); - handle = __resolve_stdin_handle(f->handle); - filetype = GetFileType(handle); - is_console_input = g_fds.stdin.handle ? f->handle == g_fds.stdin.handle - : f->handle == g_fds.p[0].handle; // the caller might be reading a single byte at a time. but we need to // be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0" @@ -103,78 +114,61 @@ StartOver: targetsize = size; } - if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) { - struct NtOverlapped overlap = {0}; - if (offset != -1) { - // pread() and pwrite() should not be called on a pipe or tty - return espipe(); + if (!pwriting && seekable) { + pthread_mutex_lock(&f->lock); + offset = f->pointer; + } + + struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0), + .Pointer = offset}; + // the win32 manual says it's important to *not* put &got here + // since for overlapped i/o, we always use GetOverlappedResult + ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); + if (!ok && GetLastError() == kNtErrorIoPending) { + BlockingOperation: + if (!nonblock) { + pt->ioverlap = &overlap; + pt->iohandle = handle; } - if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) { - // the win32 manual says it's important to *not* put &got here - // since for overlapped i/o, we always use GetOverlappedResult - ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); - if (!ok && GetLastError() == kNtErrorIoPending) { - BEGIN_BLOCKING_OPERATION; - // the i/o operation is in flight; blocking is unavoidable - // if we're in a non-blocking mode, then immediately abort - // if an interrupt is pending then we abort before waiting - // otherwise wait for i/o periodically checking interrupts - if (f->flags & O_NONBLOCK) { - sys_read_nt_abort(handle, &overlap); - } else if (_check_interrupts(kSigOpRestartable)) { - Interrupted: - abort_errno = errno; - sys_read_nt_abort(handle, &overlap); - } else { - for (;;) { - uint32_t i; - if (g_fds.stdin.inisem) { - ReleaseSemaphore(g_fds.stdin.inisem, 1, 0); - } - i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS); - if (i == kNtWaitTimeout) { - if (_check_interrupts(kSigOpRestartable)) { - goto Interrupted; - } - } else { - npassert(!i); - break; - } - } - } - ok = true; - END_BLOCKING_OPERATION; - } - if (ok) { - // overlapped is allocated on stack, so it's important we wait - // for windows to acknowledge that it's done using that memory - ok = GetOverlappedResult(handle, &overlap, &got, true); - if (!ok && GetLastError() == kNtErrorIoIncomplete) { - kprintf("you complete me\n"); - ok = true; - } - } - __imp_CloseHandle(overlap.hEvent); + if (nonblock) { + CancelIoEx(handle, &overlap); + } else if (_check_interrupts(kSigOpRestartable)) { + Interrupted: + pt->abort_errno = errno; + CancelIoEx(handle, &overlap); } else { - ok = false; + for (;;) { + uint32_t i; + if (g_fds.stdin.inisem) { + ReleaseSemaphore(g_fds.stdin.inisem, 1, 0); + } + i = WaitForSingleObject(overlap.hEvent, __SIG_IO_INTERVAL_MS); + if (i == kNtWaitTimeout) { + if (_check_interrupts(kSigOpRestartable)) { + goto Interrupted; + } + } else { + break; + } + } } - } else if (offset == -1) { - // perform simple blocking file read - ok = ReadFile(handle, targetdata, targetsize, &got, 0); - } else { - // perform pread()-style file read at particular file offset - int64_t position; - // save file pointer which windows clobbers, even for overlapped i/o - if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) { - return __winerr(); // f probably isn't seekable? + pt->ioverlap = 0; + pt->iohandle = 0; + ok = true; + } + if (ok) { + // overlapped is allocated on stack, so it's important we wait + // for windows to acknowledge that it's done using that memory + ok = GetOverlappedResult(handle, &overlap, &got, nonblock); + if (!ok && GetLastError() == kNtErrorIoIncomplete) { + goto BlockingOperation; } - struct NtOverlapped overlap = {0}; - overlap.Pointer = (void *)(uintptr_t)offset; - ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); - if (!ok && GetLastError() == kNtErrorIoPending) ok = true; - if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true); - // restore file pointer which windows clobbers, even on error - unassert(SetFilePointerEx(handle, position, 0, SEEK_SET)); + } + __imp_CloseHandle(overlap.hEvent); // __imp_ to avoid log noise + + if (!pwriting && seekable) { + if (ok) f->pointer = offset + got; + pthread_mutex_unlock(&f->lock); } if (ok) { @@ -210,7 +204,7 @@ StartOver: case kNtErrorAccessDenied: // read doesn't return EACCESS return ebadf(); // case kNtErrorOperationAborted: - errno = abort_errno; + errno = pt->abort_errno; return -1; default: return __winerr(); @@ -225,9 +219,16 @@ textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen, while (iovlen && !iov[0].iov_len) iov++, iovlen--; if (iovlen) { for (total = i = 0; i < iovlen; ++i) { + // TODO(jart): disable cancelations after first iteration if (!iov[i].iov_len) continue; rc = sys_read_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset); - if (rc == -1) return -1; + if (rc == -1) { + if (total && errno != ECANCELED) { + return total; + } else { + return -1; + } + } total += rc; if (opt_offset != -1) opt_offset += rc; if (rc < iov[i].iov_len) break; diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index e85de99d9..52230340e 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -46,9 +46,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, int64_t h; ssize_t rc; - uint32_t mem = 16384; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + uint32_t mem = 6000; volatile char *memory = alloca(mem); CheckLargeStackAllocation((char *)memory, mem); +#pragma GCC pop_options struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory; if ((h = CreateFile(path16, kNtFileReadAttributes, kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, diff --git a/libc/calls/renameat-nt.c b/libc/calls/renameat-nt.c index 3399bcc72..30ac49b83 100644 --- a/libc/calls/renameat-nt.c +++ b/libc/calls/renameat-nt.c @@ -24,6 +24,7 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/thunk/msabi.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" @@ -44,24 +45,30 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { // translate unix to windows paths - char16_t oldpath16[PATH_MAX]; - char16_t newpath16[PATH_MAX]; - if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || - __mkntpathat(newdirfd, newpath, 0, newpath16) == -1) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + struct { + char16_t oldpath16[PATH_MAX]; + char16_t newpath16[PATH_MAX]; + } M; + CheckLargeStackAllocation(&M, sizeof(M)); +#pragma GCC pop_options + if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) == -1 || + __mkntpathat(newdirfd, newpath, 0, M.newpath16) == -1) { return -1; } // strip trailing slash // win32 will einval otherwise // normally this is handled by __fix_enotdir() - bool old_must_be_dir = StripTrailingSlash(oldpath16); - bool new_must_be_dir = StripTrailingSlash(newpath16); + bool old_must_be_dir = StripTrailingSlash(M.oldpath16); + bool new_must_be_dir = StripTrailingSlash(M.newpath16); // test for some known error conditions ahead of time // the enotdir check can't be done reactively // ideally we should resolve symlinks first - uint32_t oldattr = __imp_GetFileAttributesW(oldpath16); - uint32_t newattr = __imp_GetFileAttributesW(newpath16); + uint32_t oldattr = __imp_GetFileAttributesW(M.oldpath16); + uint32_t newattr = __imp_GetFileAttributesW(M.newpath16); if ((old_must_be_dir && oldattr != -1u && !(oldattr & kNtFileAttributeDirectory)) || (new_must_be_dir && newattr != -1u && @@ -78,16 +85,16 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, } else if ((oldattr & kNtFileAttributeDirectory) && (newattr & kNtFileAttributeDirectory)) { // both old and new are directories - if (!__imp_RemoveDirectoryW(newpath16) && + if (!__imp_RemoveDirectoryW(M.newpath16) && GetLastError() == kNtErrorDirNotEmpty) { return enotempty(); } } } - if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { + if (MoveFileEx(M.oldpath16, M.newpath16, kNtMovefileReplaceExisting)) { return 0; } else { - return __fix_enotdir3(-1, oldpath16, newpath16); + return __fix_enotdir3(-1, M.oldpath16, M.newpath16); } } diff --git a/libc/calls/restoretty.c b/libc/calls/restoretty.c index ad69f2cf4..9b6bf31c0 100644 --- a/libc/calls/restoretty.c +++ b/libc/calls/restoretty.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/cp.internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/syscall-sysv.internal.h" @@ -60,7 +61,9 @@ void __restore_tty(void) { int e; if (__isrestorable && !__isworker && !__nocolor) { e = errno; + BEGIN_CANCELLATION_POINT; sys_write(0, ANSI_RESTORE, __strlen(ANSI_RESTORE)); + END_CANCELLATION_POINT; tcsetattr(0, TCSAFLUSH, &__oldtermios); errno = e; } diff --git a/libc/calls/sig.c b/libc/calls/sig.c new file mode 100644 index 000000000..c06db87f1 --- /dev/null +++ b/libc/calls/sig.c @@ -0,0 +1,475 @@ +/*-*- 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/sysv/consts/sig.h" +#include "ape/sections.internal.h" +#include "libc/atomic.h" +#include "libc/calls/blockcancel.internal.h" +#include "libc/calls/blocksigs.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/describebacktrace.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/popcnt.h" +#include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" +#include "libc/nt/console.h" +#include "libc/nt/enum/context.h" +#include "libc/nt/enum/exceptionhandleractions.h" +#include "libc/nt/enum/signal.h" +#include "libc/nt/enum/status.h" +#include "libc/nt/errors.h" +#include "libc/nt/runtime.h" +#include "libc/nt/signals.h" +#include "libc/nt/struct/context.h" +#include "libc/nt/struct/ntexceptionpointers.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/ss.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" + +#ifdef __x86_64__ + +struct SignalFrame { + struct PosixThread *pt; + struct NtContext *nc; + unsigned rva; + unsigned flags; + siginfo_t si; +}; + +struct ContextFrame { + struct SignalFrame sf; + struct NtContext nc; +}; + +void __stack_call(int, siginfo_t *, void *, long, + void (*)(int, siginfo_t *, void *), void *); + +static textwindows bool __sig_ignored_by_default(int sig) { + return sig == SIGURG || // + sig == SIGCONT || // + sig == SIGCHLD || // + sig == SIGWINCH; +} + +textwindows bool __sig_ignored(int sig) { + return __sighandrvas[sig] == (intptr_t)SIG_IGN || + (__sighandrvas[sig] == (intptr_t)SIG_DFL && + __sig_ignored_by_default(sig)); +} + +static textwindows bool __sig_should_use_altstack(unsigned flags, + struct CosmoTib *tib) { + return (flags & SA_ONSTACK) && // + tib->tib_sigstack_size && // + !(tib->tib_sigstack_flags & (SS_DISABLE | SS_ONSTACK)); +} + +static textwindows wontreturn void __sig_terminate(int sig) { + TerminateThisProcess(sig); +} + +static textwindows bool __sig_start(struct PosixThread *pt, int sig, + unsigned *rva, unsigned *flags) { + *rva = __sighandrvas[sig]; + *flags = __sighandflags[sig]; + if (*rva == (intptr_t)SIG_IGN || + (*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) { + STRACE("ignoring %G", sig); + return false; + } + if (pt->tib->tib_sigmask & (1ull << (sig - 1))) { + STRACE("tid %d masked %G delivering to tib_sigpending", _pthread_tid(pt), + sig); + pt->tib->tib_sigpending |= 1ull << (sig - 1); + return false; + } + if (*rva == (intptr_t)SIG_DFL) { + STRACE("terminating on %G due to no handler", sig); + __sig_terminate(sig); + } + if (*flags & SA_RESETHAND) { + STRACE("resetting %G handler", sig); + __sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL; + } + return true; +} + +static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx, + unsigned rva, unsigned flags, + struct CosmoTib *tib) { + sigaction_f handler; + handler = (sigaction_f)(__executable_start + rva); + ++__sig.count; + if (__sig_should_use_altstack(flags, tib)) { + tib->tib_sigstack_flags |= SS_ONSTACK; + __stack_call(sig, si, ctx, 0, handler, + tib->tib_sigstack_addr + tib->tib_sigstack_size); + tib->tib_sigstack_flags &= ~SS_ONSTACK; + } else { + handler(sig, si, ctx); + } +} + +textwindows int __sig_raise(int sig, int sic) { + unsigned rva, flags; + struct CosmoTib *tib = __get_tls(); + struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread; + ucontext_t ctx = {.uc_sigmask.__bits[0] = tib->tib_sigmask}; + if (!__sig_start(pt, sig, &rva, &flags)) return 0; + siginfo_t si = {.si_signo = sig, .si_code = sic}; + struct NtContext nc; + nc.ContextFlags = kNtContextAll; + GetThreadContext(GetCurrentThread(), &nc); + _ntcontext2linux(&ctx, &nc); + STRACE("raising %G", sig); + if (!(flags & SA_NODEFER)) { + tib->tib_sigmask |= 1ull << (sig - 1); + } + __sig_call(sig, &si, &ctx, rva, flags, tib); + tib->tib_sigmask = ctx.uc_sigmask.__bits[0]; + return (flags & SA_RESTART) ? 2 : 1; +} + +textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) { + atomic_int *futex; + if (_weaken(WakeByAddressSingle) && + (futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) { + _weaken(WakeByAddressSingle)(futex); + } else if (!(flags & SA_RESTART) && pt->iohandle > 0) { + pt->abort_errno = EINTR; + if (!CancelIoEx(pt->iohandle, pt->ioverlap)) { + int err = GetLastError(); + if (err != kNtErrorNotFound) { + STRACE("CancelIoEx() failed w/ %d", err); + } + } + } else if (pt->pt_flags & PT_INSEMAPHORE) { + pt->abort_errno = EINTR; + if (!ReleaseSemaphore(pt->semaphore, 1, 0)) { + STRACE("ReleaseSemaphore() failed w/ %d", GetLastError()); + } + } +} + +static textwindows wontreturn void __sig_panic(const char *msg) { +#ifndef TINY + char s[128], *p = s; + p = stpcpy(p, "sig panic: "); + p = stpcpy(p, msg); + p = stpcpy(p, " failed w/ "); + p = FormatInt32(p, GetLastError()); + *p++ = '\n'; + WriteFile(GetStdHandle(kNtStdErrorHandle), s, p - s, 0, 0); +#endif + TerminateThisProcess(SIGVTALRM); +} + +static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) { + ucontext_t ctx = {0}; + sigaction_f handler = (sigaction_f)(__executable_start + sf->rva); + STRACE("__sig_tramp(%G, %t)", sf->si.si_signo, handler); + _ntcontext2linux(&ctx, sf->nc); + ctx.uc_sigmask.__bits[0] = sf->pt->tib->tib_sigmask; + if (!(sf->flags & SA_NODEFER)) { + sf->pt->tib->tib_sigmask |= 1ull << (sf->si.si_signo - 1); + } + ++__sig.count; + handler(sf->si.si_signo, &sf->si, &ctx); + sf->pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0]; + _ntlinux2context(sf->nc, &ctx); + SetThreadContext(GetCurrentThread(), sf->nc); + __sig_panic("SetThreadContext(GetCurrentThread)"); +} + +static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { + uintptr_t th; + unsigned rva, flags; + if (!__sig_start(pt, sig, &rva, &flags)) return 0; + th = _pthread_syshand(pt); + uint32_t old_suspend_count; + old_suspend_count = SuspendThread(th); + if (old_suspend_count == -1u) { + STRACE("SuspendThread failed w/ %d", GetLastError()); + return ESRCH; + } + if (old_suspend_count) { + STRACE("kill contention of %u on tid %d", old_suspend_count, + _pthread_tid(pt)); + pt->tib->tib_sigpending |= 1ull << (sig - 1); + return 0; + } + struct NtContext nc; + nc.ContextFlags = kNtContextAll; + if (!GetThreadContext(th, &nc)) { + STRACE("GetThreadContext failed w/ %d", GetLastError()); + return ESRCH; + } + uintptr_t sp; + if (__sig_should_use_altstack(flags, pt->tib)) { + pt->tib->tib_sigstack_flags |= SS_ONSTACK; + sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size; + pt->tib->tib_sigstack_flags &= ~SS_ONSTACK; + } else { + sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16; + } + struct ContextFrame *cf = (struct ContextFrame *)sp; + bzero(&cf->sf.si, sizeof(cf->sf.si)); + memcpy(&cf->nc, &nc, sizeof(nc)); + cf->sf.pt = pt; + cf->sf.rva = rva; + cf->sf.nc = &cf->nc; + cf->sf.flags = flags; + cf->sf.si.si_code = sic; + cf->sf.si.si_signo = sig; + *(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip; + nc.Rip = (intptr_t)__sig_tramp; + nc.Rdi = (intptr_t)&cf->sf; + nc.Rsp = sp; + if (!SetThreadContext(th, &nc)) { + STRACE("SetThreadContext failed w/ %d", GetLastError()); + return ESRCH; + } + ResumeThread(th); // doesn't actually resume if doing blocking i/o + __sig_cancel(pt, flags); // we can wake it up immediately by canceling it + return 0; +} + +textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) { + int rc; + BLOCK_SIGNALS; + BLOCK_CANCELLATIONS; + rc = __sig_killer(pt, sig, sic); + ALLOW_CANCELLATIONS; + ALLOW_SIGNALS; + return rc; +} + +textwindows void __sig_generate(int sig, int sic) { + struct Dll *e; + struct PosixThread *pt, *mark = 0; + if (__sig_ignored(sig)) { + STRACE("ignoring %G", sig); + return; + } + if (__sighandrvas[sig] == (intptr_t)SIG_DFL) { + STRACE("terminating on %G due to no handler", sig); + __sig_terminate(sig); + } + pthread_spin_lock(&_pthread_lock); + for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { + pt = POSIXTHREAD_CONTAINER(e); + if (atomic_load_explicit(&pt->status, memory_order_acquire) < + kPosixThreadTerminated && + !(pt->tib->tib_sigmask & (1ull << (sig - 1)))) { + mark = pt; + break; + } + } + pthread_spin_unlock(&_pthread_lock); + if (mark) { + __sig_kill(mark, sig, sic); + } else { + STRACE("all threads block %G so adding to pending signals of process", sig); + __sig.pending |= sig; + } +} + +static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) { + switch (ep->ExceptionRecord->ExceptionCode) { + case kNtSignalBreakpoint: + *code = TRAP_BRKPT; + return SIGTRAP; + case kNtSignalIllegalInstruction: + *code = ILL_ILLOPC; + return SIGILL; + case kNtSignalPrivInstruction: + *code = ILL_PRVOPC; + return SIGILL; + case kNtSignalGuardPage: + case kNtSignalInPageError: + case kNtStatusStackOverflow: + *code = SEGV_MAPERR; + return SIGSEGV; + case kNtSignalAccessViolation: + *code = SEGV_ACCERR; + return SIGSEGV; + case kNtSignalInvalidHandle: + case kNtSignalInvalidParameter: + case kNtSignalAssertionFailure: + *code = SI_USER; + return SIGABRT; + case kNtStatusIntegerOverflow: + *code = FPE_INTOVF; + return SIGFPE; + case kNtSignalFltDivideByZero: + *code = FPE_FLTDIV; + return SIGFPE; + case kNtSignalFltOverflow: + *code = FPE_FLTOVF; + return SIGFPE; + case kNtSignalFltUnderflow: + *code = FPE_FLTUND; + return SIGFPE; + case kNtSignalFltInexactResult: + *code = FPE_FLTRES; + return SIGFPE; + case kNtSignalFltDenormalOperand: + case kNtSignalFltInvalidOperation: + case kNtSignalFltStackCheck: + case kNtSignalIntegerDivideByZero: + case kNtSignalFloatMultipleFaults: + case kNtSignalFloatMultipleTraps: + *code = FPE_FLTINV; + return SIGFPE; + case kNtSignalDllNotFound: + case kNtSignalOrdinalNotFound: + case kNtSignalEntrypointNotFound: + case kNtSignalDllInitFailed: + *code = SI_KERNEL; + return SIGSYS; + default: + *code = ep->ExceptionRecord->ExceptionCode; + return SIGSEGV; + } +} + +// abashed the devil stood, and felt how awful goodness is +__msabi unsigned __sig_crash(struct NtExceptionPointers *ep) { + int code, sig = __sig_crash_sig(ep, &code); + STRACE("win32 vectored exception 0x%08Xu raising %G " + "cosmoaddr2line %s %lx %s", + ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name, + ep->ContextRecord->Rip, + DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp)); + if (sig == SIGTRAP) { + ep->ContextRecord->Rip++; + if (__sig_ignored(sig)) { + return kNtExceptionContinueExecution; + } + } + struct PosixThread *pt = _pthread_self(); + siginfo_t si = {.si_signo = sig, + .si_code = code, + .si_addr = ep->ExceptionRecord->ExceptionAddress}; + unsigned rva = __sighandrvas[sig]; + unsigned flags = __sighandflags[sig]; + if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) { +#ifndef TINY + intptr_t hStderr; + char sigbuf[21], s[128], *p; + hStderr = GetStdHandle(kNtStdErrorHandle); + p = stpcpy(s, "Terminating on uncaught "); + p = stpcpy(p, strsignal_r(sig, sigbuf)); + p = stpcpy(p, ". Pass --strace and/or ShowCrashReports() for details.\n"); + WriteFile(hStderr, s, p - s, 0, 0); +#endif + __sig_terminate(sig); + } + if (flags & SA_RESETHAND) { + STRACE("resetting %G handler", sig); + __sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL; + } + ucontext_t ctx = {0}; + _ntcontext2linux(&ctx, ep->ContextRecord); + ctx.uc_sigmask.__bits[0] = pt->tib->tib_sigmask; + __sig_call(sig, &si, &ctx, rva, flags, pt->tib); + pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0]; + _ntlinux2context(ep->ContextRecord, &ctx); + return kNtExceptionContinueExecution; +} + +static textwindows int __sig_console_sig(uint32_t dwCtrlType) { + switch (dwCtrlType) { + case kNtCtrlCEvent: + return SIGINT; + case kNtCtrlBreakEvent: + return SIGQUIT; + case kNtCtrlCloseEvent: + case kNtCtrlLogoffEvent: // only received by services + case kNtCtrlShutdownEvent: // only received by services + return SIGHUP; + default: + return SIGSTKFLT; + } +} + +__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) { + struct CosmoTib tls; + __bootstrap_tls(&tls, __builtin_frame_address(0)); + __sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL); + return true; +} + +static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib, + const char *thing, int id) { + bool handler_was_called = false; + uint64_t pending, masked, deliverable; + pending = atomic_load_explicit(sigs, memory_order_acquire); + masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire); + deliverable = pending & ~masked; + POLLTRACE("%s %d blocks %d sigs w/ %d pending and %d deliverable", thing, id, + popcnt(masked), popcnt(pending), popcnt(deliverable)); + if (deliverable) { + for (int sig = 1; sig <= 64; ++sig) { + if ((deliverable & (1ull << (sig - 1))) && + atomic_fetch_and(sigs, ~(1ull << (sig - 1))) & (1ull << (sig - 1))) { + handler_was_called |= __sig_raise(sig, SI_KERNEL); + } + } + } + return handler_was_called; +} + +// returns 0 if no signal handlers were called, otherwise a bitmask +// consisting of `1` which means a signal handler was invoked which +// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART +// handlers were called (or `3` if both were the case). +textwindows int __sig_check(void) { + bool handler_was_called = false; + struct CosmoTib *tib = __get_tls(); + handler_was_called |= + __sig_checkem(&tib->tib_sigpending, tib, "tid", tib->tib_tid); + handler_was_called |= __sig_checkem(&__sig.pending, tib, "pid", getpid()); + POLLTRACE("__sig_check() → %hhhd", handler_was_called); + return handler_was_called; +} + +textstartup void __sig_init(void) { + if (!IsWindows()) return; + AddVectoredExceptionHandler(true, (void *)__sig_crash); + SetConsoleCtrlHandler((void *)__sig_console, true); +} + +const void *const __sig_ctor[] initarray = {__sig_init}; + +#endif /* __x86_64__ */ diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 582b008ce..083b4c20d 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -1,39 +1,34 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ #include "libc/calls/struct/sigset.h" -#include "libc/calls/ucontext.h" -#include "libc/nt/struct/context.h" +#include "libc/thread/posixthread.internal.h" -#define __SIG_QUEUE_LENGTH 32 -#define __SIG_POLLING_INTERVAL_MS 20 +#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */ +#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */ +#define __SIG_PROC_INTERVAL_MS 1000 /* process waiting also pretty good */ +#define __SIG_IO_INTERVAL_MS 1000 /* read/write cancel/notify is good */ +#define __SIG_POLL_INTERVAL_MS 20 /* poll on windows is dumpster fire */ #define __SIG_LOGGING_INTERVAL_MS 1700 +#define __SIG_QUEUE_LENGTH 32 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -struct Delivery { - int ops; - int sig; - int sic; - struct NtContext *nc; -}; - struct Signals { - uint64_t mask; - uint64_t pending; - uint64_t count; + _Atomic(uint64_t) pending; + _Atomic(uint64_t) count; }; extern struct Signals __sig; -bool __sig_check(int); -bool __sig_is_core(int); -bool __sig_is_ignored(int); -bool __sig_handle(int, int, int, ucontext_t *); +bool __sig_ignored(int); +int __sig_check(void); +int __sig_kill(struct PosixThread *, int, int); int __sig_mask(int, const sigset_t *, sigset_t *); -bool __sig_deliver(int, int, int, ucontext_t *); -int __sig_tramp(struct Delivery *); -bool32 __sig_notify(int, int); +int __sig_raise(int, int); +void __sig_cancel(struct PosixThread *, unsigned); +void __sig_generate(int, int); +void __sig_init(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index a6bd1dff6..4f285e4e0 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -49,13 +49,8 @@ #include "libc/sysv/errfuns.h" #include "libc/thread/tls.h" -#ifdef SYSDEBUG -__static_yoink("strsignal"); // for kprintf() -#endif - #if SupportsWindows() -__static_yoink("_init_onntconsoleevent"); -__static_yoink("_init_wincrash"); +__static_yoink("__sig_ctor"); #endif #define SA_RESTORER 0x04000000 @@ -250,23 +245,21 @@ static int __sigaction(int sig, const struct sigaction *act, } else { if (oldact) { bzero(oldact, sizeof(*oldact)); - } - rc = 0; - } - if (rc != -1 && !__vforked) { - if (oldact) { oldrva = __sighandrvas[sig]; + oldact->sa_flags = __sighandflags[sig]; oldact->sa_sigaction = (sigaction_f)(oldrva < kSigactionMinRva ? oldrva : (intptr_t)&__executable_start + oldrva); } + rc = 0; + } + if (rc != -1 && !__vforked) { if (act) { __sighandrvas[sig] = rva; __sighandflags[sig] = act->sa_flags; if (IsWindows()) { - if (rva == (intptr_t)SIG_IGN || - (rva == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) { + if (__sig_ignored(sig)) { __sig.pending &= ~(1ull << (sig - 1)); if (__tls_enabled) { __get_tls()->tib_sigpending &= ~(1ull << (sig - 1)); @@ -281,7 +274,9 @@ static int __sigaction(int sig, const struct sigaction *act, /** * Installs handler for kernel interrupt to thread, e.g.: * - * void GotCtrlC(int sig, siginfo_t *si, void *ctx); + * void GotCtrlC(int sig, siginfo_t *si, void *arg) { + * ucontext_t *ctx = arg; + * } * struct sigaction sa = {.sa_sigaction = GotCtrlC, * .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO}; * CHECK_NE(-1, sigaction(SIGINT, &sa, NULL)); diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c index c593fbccb..d30ad39af 100644 --- a/libc/calls/sigaltstack.c +++ b/libc/calls/sigaltstack.c @@ -24,8 +24,10 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/ss.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/tls.h" static void sigaltstack2bsd(struct sigaltstack_bsd *bsd, const struct sigaltstack *linux) { @@ -53,14 +55,63 @@ static void sigaltstack2linux(struct sigaltstack *linux, linux->ss_size = size; } +static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu, + struct sigaltstack *old) { + struct CosmoTib *tib; + tib = __get_tls(); + if (old) { + old->ss_sp = tib->tib_sigstack_addr; + old->ss_size = tib->tib_sigstack_size; + old->ss_flags = tib->tib_sigstack_flags; + } + if (neu) { + tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16); + tib->tib_sigstack_size = ROUNDDOWN(neu->ss_size, 16); + tib->tib_sigstack_flags &= SS_ONSTACK; + tib->tib_sigstack_flags |= neu->ss_flags & SS_DISABLE; + } + return 0; +} + +static int sigaltstack_sysv(const struct sigaltstack *neu, + struct sigaltstack *old) { + void *b; + const void *a; + struct sigaltstack_bsd bsd; + if (IsLinux()) { + a = neu; + b = old; + } else { + if (neu) { + sigaltstack2bsd(&bsd, neu); + a = &bsd; + } else { + a = 0; + } + if (old) { + b = &bsd; + } else { + b = 0; + } + } + if (!sys_sigaltstack(a, b)) { + if (IsBsd() && old) { + sigaltstack2linux(old, &bsd); + } + return 0; + } else { + return -1; + } +} + /** * Sets and/or gets alternate signal stack, e.g. * * struct sigaction sa; * struct sigaltstack ss; * ss.ss_flags = 0; - * ss.ss_sp = NewCosmoStack(); - * ss.ss_size = GetStackSize(); + * ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192; + * ss.ss_sp = malloc(ss.ss_size); * sigaltstack(&ss, 0); * sigemptyset(&sa.ss_mask); * sa.sa_flags = SA_ONSTACK; @@ -75,41 +126,20 @@ static void sigaltstack2linux(struct sigaltstack *linux, */ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { int rc; - void *b; - const void *a; - struct sigaltstack_bsd bsd; if (IsAsan() && ((old && !__asan_is_valid(old, sizeof(*old))) || (neu && !__asan_is_valid(neu, sizeof(*neu))))) { rc = efault(); - } else if (neu && neu->ss_size < MINSIGSTKSZ) { + } else if (neu && ((neu->ss_size >> 32) || // + (neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) { + return einval(); + } else if (neu && neu->ss_size < __get_minsigstksz()) { rc = enomem(); } else if (IsLinux() || IsBsd()) { - if (IsLinux()) { - a = neu; - b = old; - } else { - if (neu) { - sigaltstack2bsd(&bsd, neu); - a = &bsd; - } else { - a = 0; - } - if (old) { - b = &bsd; - } else { - b = 0; - } - } - if ((rc = sys_sigaltstack(a, b)) != -1) { - if (IsBsd() && old) { - sigaltstack2linux(old, &bsd); - } - rc = 0; - } else { - rc = -1; + if (!(rc = sigaltstack_sysv(neu, old))) { + sigaltstack_cosmo(neu, old); } } else { - rc = enosys(); + rc = sigaltstack_cosmo(neu, old); } STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstk(0, neu), DescribeSigaltstk(0, old), rc); diff --git a/libc/calls/sigblockall.c b/libc/calls/sigblockall.c index a0a516bfc..f29226264 100644 --- a/libc/calls/sigblockall.c +++ b/libc/calls/sigblockall.c @@ -17,11 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" -#include "libc/log/libfatal.internal.h" -#include "libc/str/str.h" -dontasan sigset_t _sigblockall(void) { - sigset_t ss; - __repstosb(&ss, -1, sizeof(ss)); +sigset_t _sigblockall(void) { + sigset_t ss = {{-1, -1}}; return _sigsetmask(ss); } diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c deleted file mode 100644 index 0e7dc36db..000000000 --- a/libc/calls/sigchld-nt.c +++ /dev/null @@ -1,80 +0,0 @@ -/*-*- 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/internal.h" -#include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/cosmo.h" -#include "libc/nt/enum/wait.h" -#include "libc/nt/runtime.h" -#include "libc/nt/synchronization.h" -#include "libc/nt/thread.h" -#include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" -#include "libc/thread/tls.h" -#include "libc/thread/tls2.internal.h" -#ifdef __x86_64__ - -intptr_t __sigchld_thread; -static atomic_uint __sigchld_once; -static struct CosmoTib __sigchld_tls; - -static textwindows bool __sigchld_check(void) { - bool should_signal = false; - for (;;) { - int pids[64]; - int64_t handles[64]; - uint32_t n = __sample_pids(pids, handles, true); - if (!n) return should_signal; - uint32_t i = WaitForMultipleObjects(n, handles, false, 0); - if (i == kNtWaitFailed) return should_signal; - if (i == kNtWaitTimeout) return should_signal; - i &= ~kNtWaitAbandoned; - if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) && - (__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) { - CloseHandle(handles[i]); - __releasefd(pids[i]); - } else { - g_fds.p[pids[i]].zombie = true; - } - should_signal = true; - } -} - -static textwindows uint32_t __sigchld_worker(void *arg) { - __set_tls_win32(&__sigchld_tls); - for (;;) { - if (__sigchld_check()) { - __sig_notify(SIGCHLD, CLD_EXITED); - } - SleepEx(100, false); - } - return 0; -} - -static textwindows void __sigchld_init(void) { - __sigchld_thread = CreateThread(0, 65536, __sigchld_worker, 0, 0, 0); -} - -void _init_sigchld(void) { - cosmo_once(&__sigchld_once, __sigchld_init); -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/sigenter-freebsd.c b/libc/calls/sigenter-freebsd.c index d57336803..1fb236112 100644 --- a/libc/calls/sigenter-freebsd.c +++ b/libc/calls/sigenter-freebsd.c @@ -29,6 +29,7 @@ #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -36,11 +37,15 @@ privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, struct ucontext_freebsd *ctx) { - int rva, flags; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" struct Goodies { ucontext_t uc; siginfo_t si; } g; + CheckLargeStackAllocation(&g, sizeof(g)); +#pragma GCC pop_options + int rva, flags; rva = __sighandrvas[sig]; if (rva >= kSigactionMinRva) { flags = __sighandflags[sig]; diff --git a/libc/calls/sigenter-linux.c b/libc/calls/sigenter-linux.c index 19aaad697..ee97fcc7c 100644 --- a/libc/calls/sigenter-linux.c +++ b/libc/calls/sigenter-linux.c @@ -32,7 +32,7 @@ #ifdef __x86_64__ privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) { - int i, rva, flags; + int rva, flags; rva = __sighandrvas[sig]; if (rva >= kSigactionMinRva) { flags = __sighandflags[sig]; @@ -40,10 +40,6 @@ privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) { // https://github.com/microsoft/WSL/issues/2555 if ((flags & SA_SIGINFO) && UNLIKELY(!ctx->uc_mcontext.fpregs)) { ctx->uc_mcontext.fpregs = &ctx->__fpustate; - for (i = 0; i < 8; ++i) { - long double nan = NAN; - __memcpy(ctx->__fpustate.st + i, &nan, 16); - } } ((sigaction_f)(__executable_start + rva))(sig, info, ctx); } diff --git a/libc/calls/sigenter-netbsd.c b/libc/calls/sigenter-netbsd.c index c8f004596..b5d600615 100644 --- a/libc/calls/sigenter-netbsd.c +++ b/libc/calls/sigenter-netbsd.c @@ -29,6 +29,7 @@ #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -36,8 +37,12 @@ privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, struct ucontext_netbsd *ctx) { - int rva, flags; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" ucontext_t uc; + CheckLargeStackAllocation(&uc, sizeof(uc)); +#pragma GCC pop_options + int rva, flags; struct siginfo si2; rva = __sighandrvas[sig]; if (rva >= kSigactionMinRva) { diff --git a/libc/calls/sigenter-openbsd.c b/libc/calls/sigenter-openbsd.c index 8bff18906..2576cf8be 100644 --- a/libc/calls/sigenter-openbsd.c +++ b/libc/calls/sigenter-openbsd.c @@ -29,6 +29,7 @@ #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -36,11 +37,15 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, struct ucontext_openbsd *ctx) { - int rva, flags; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" struct Goodies { ucontext_t uc; struct siginfo si; } g; + CheckLargeStackAllocation(&g, sizeof(g)); +#pragma GCC pop_options + int rva, flags; rva = __sighandrvas[sig]; if (rva >= kSigactionMinRva) { flags = __sighandflags[sig]; diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index d557a3f07..e079fb7fe 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -29,6 +29,7 @@ #include "libc/intrin/repstosb.h" #include "libc/log/libfatal.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -488,11 +489,15 @@ static privileged void linuxssefpustate2xnu( privileged void __sigenter_xnu(void *fn, int infostyle, int sig, struct siginfo_xnu *xnuinfo, struct __darwin_ucontext *xnuctx) { - int rva, flags; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" struct Goodies { ucontext_t uc; siginfo_t si; } g; + CheckLargeStackAllocation(&g, sizeof(g)); +#pragma GCC pop_options + int rva, flags; rva = __sighandrvas[sig]; if (rva >= kSigactionMinRva) { flags = __sighandflags[sig]; diff --git a/libc/calls/siginfo2cosmo.c b/libc/calls/siginfo2cosmo.c index e3aa6ab9a..6ca045844 100644 --- a/libc/calls/siginfo2cosmo.c +++ b/libc/calls/siginfo2cosmo.c @@ -98,6 +98,8 @@ privileged void __siginfo2cosmo(struct siginfo *si, si->si_addr = si_addr; } else if (si_signo == SIGCHLD) { si->si_status = si_status; + si->si_pid = si_pid; + si->si_uid = si_uid; } else if (si_signo == SIGALRM) { si->si_timerid = si_timerid; si->si_overrun = si_overrun; diff --git a/libc/calls/__sig_mask.c b/libc/calls/sigmask.c similarity index 70% rename from libc/calls/__sig_mask.c rename to libc/calls/sigmask.c index f4fbbc53b..4aca8dd37 100644 --- a/libc/calls/__sig_mask.c +++ b/libc/calls/sigmask.c @@ -19,6 +19,8 @@ #include "libc/assert.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigset.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/weaken.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/thread/tls.h" @@ -28,37 +30,43 @@ #define GetSigBit(x) (1ull << (((x)-1) & 63)) textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) { - uint64_t x, y, *mask; - if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) { - if (__tls_enabled) { - mask = &__get_tls()->tib_sigmask; - } else { - mask = &__sig.mask; - } - if (old) { - old->__bits[0] = *mask; - old->__bits[1] = 0; - } - if (neu) { - x = *mask; - y = neu->__bits[0]; - if (how == SIG_BLOCK) { - x |= y; - } else if (how == SIG_UNBLOCK) { - x &= ~y; - } else { - x = y; - } - x &= ~(0 -#define M(x) | GetSigBit(x) -#include "libc/intrin/sigisprecious.inc" - ); - *mask = x; - } - return 0; - } else { + + // validate api usage + if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) { return einval(); } + + // get address of sigset to modify + _Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask; + + // these signals are precious to cosmopolitan + uint64_t precious = 0 +#define M(x) | GetSigBit(x) +#include "libc/intrin/sigisprecious.inc" + ; + + // handle read-only case + uint64_t oldmask; + if (neu) { + uint64_t input = neu->__bits[0] & ~precious; + if (how == SIG_BLOCK) { + oldmask = atomic_fetch_or_explicit(mask, input, memory_order_acq_rel); + } else if (how == SIG_UNBLOCK) { + oldmask = atomic_fetch_and_explicit(mask, input, memory_order_acq_rel); + } else { // SIG_SETMASK + oldmask = atomic_exchange_explicit(mask, input, memory_order_acq_rel); + } + } else { + oldmask = atomic_load_explicit(mask, memory_order_acquire); + } + + // return old signal mask to caller + if (old) { + old->__bits[0] = oldmask; + old->__bits[1] = 0; + } + + return 0; } #endif /* __x86_64__ */ diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index 787e22dd1..082465cd1 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -60,7 +60,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) { } else if (IsMetal() || IsWindows()) { rc = __sig_mask(how, opt_set, &old); if (_weaken(__sig_check)) { - _weaken(__sig_check)(kSigOpRestartable); + _weaken(__sig_check)(); } } else { rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0); diff --git a/libc/calls/sigsetmask.c b/libc/calls/sigsetmask.c index 1ee6af22a..3c1760e4a 100644 --- a/libc/calls/sigsetmask.c +++ b/libc/calls/sigsetmask.c @@ -16,19 +16,18 @@ │ 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/sig.internal.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/dce.h" #include "libc/sysv/consts/sig.h" -dontasan sigset_t _sigsetmask(sigset_t neu) { +sigset_t _sigsetmask(sigset_t neu) { sigset_t res; if (IsMetal() || IsWindows()) { __sig_mask(SIG_SETMASK, &neu, &res); } else { - npassert(!sys_sigprocmask(SIG_SETMASK, &neu, &res)); + sys_sigprocmask(SIG_SETMASK, &neu, &res); } return res; } diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c index 3f8f60510..a3a1e5e69 100644 --- a/libc/calls/sigsuspend.c +++ b/libc/calls/sigsuspend.c @@ -16,19 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/bo.internal.h" -#include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/log/backtrace.internal.h" -#include "libc/nt/errors.h" #include "libc/nt/synchronization.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" @@ -50,7 +46,6 @@ int sigsuspend(const sigset_t *ignore) { int rc; const sigset_t *arg; sigset_t save, mask = {0}; - STRACE("sigsuspend(%s) → ...", DescribeSigset(0, ignore)); BEGIN_CANCELLATION_POINT; if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) { @@ -74,36 +69,26 @@ int sigsuspend(const sigset_t *ignore) { rc = sys_sigsuspend(arg, 8); } else { __sig_mask(SIG_SETMASK, arg, &save); -#if defined(SYSDEBUG) && _POLLTRACE - long ms = 0; - long totoms = 0; -#endif - BEGIN_BLOCKING_OPERATION; - do { - if ((rc = _check_interrupts(0))) { + while (!(rc = _check_interrupts(0))) { + struct PosixThread *pt; + pt = _pthread_self(); + pt->abort_errno = 0; + pt->pt_flags |= PT_INSEMAPHORE; + WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS); + pt->pt_flags &= ~PT_INSEMAPHORE; + if (pt->abort_errno) { + errno = pt->abort_errno; + rc = -1; break; } - if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) { - POLLTRACE("IOCP EINTR"); - continue; - } -#if defined(SYSDEBUG) && _POLLTRACE - ms += __SIG_POLLING_INTERVAL_MS; - if (ms >= __SIG_LOGGING_INTERVAL_MS) { - totoms += ms, ms = 0; - POLLTRACE("... sigsuspending for %'lums...", totoms); - } -#endif - } while (1); - END_BLOCKING_OPERATION; + } __sig_mask(SIG_SETMASK, &save, 0); } } else { - // TODO(jart): sigsuspend metal support rc = enosys(); } END_CANCELLATION_POINT; - STRACE("...sigsuspend → %d% m", rc); + STRACE("sigsuspend(%s) → %d% m", DescribeSigset(0, ignore), rc); return rc; } diff --git a/libc/calls/sigwinch-nt.c b/libc/calls/sigwinch-nt.c index 7aa08c0b7..d629bef74 100644 --- a/libc/calls/sigwinch-nt.c +++ b/libc/calls/sigwinch-nt.c @@ -19,12 +19,16 @@ #include "libc/atomic.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/cosmo.h" #include "libc/nt/console.h" +#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/thread/tls.h" @@ -37,6 +41,8 @@ static atomic_uint __sigwinch_once; static struct CosmoTib __sigwinch_tls; static textwindows unsigned __get_console_size(void) { + unsigned res = -1u; + __fds_lock(); for (int fd = 1; fd < 10; ++fd) { intptr_t hConsoleOutput; if (g_fds.p[fd].kind == kFdConsole) { @@ -48,20 +54,22 @@ static textwindows unsigned __get_console_size(void) { if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) { unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1; unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1; - return (unsigned)yn << 16 | xn; + res = (unsigned)yn << 16 | xn; + break; } } - return -1u; + __fds_unlock(); + return res; } -static textwindows uint32_t __sigwinch_worker(void *arg) { - __set_tls_win32(&__sigwinch_tls); +static textwindows dontinstrument uint32_t __sigwinch_worker(void *arg) { + __bootstrap_tls(&__sigwinch_tls, __builtin_frame_address(0)); for (;;) { unsigned old = __sigwinch_size; unsigned neu = __get_console_size(); if (neu != old) { __sigwinch_size = neu; - __sig_notify(SIGWINCH, SI_KERNEL); + __sig_generate(SIGWINCH, SI_KERNEL); } SleepEx(25, false); } @@ -69,8 +77,10 @@ static textwindows uint32_t __sigwinch_worker(void *arg) { } static textwindows void __sigwinch_init(void) { + __enable_threads(); __sigwinch_size = __get_console_size(); - __sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0, 0, 0); + __sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0, + kNtStackSizeParamIsAReservation, 0); } textwindows void _init_sigwinch(void) { diff --git a/libc/calls/state.internal.h b/libc/calls/state.internal.h index 054ea5bfe..be72f6afb 100644 --- a/libc/calls/state.internal.h +++ b/libc/calls/state.internal.h @@ -12,9 +12,9 @@ extern unsigned __sighandrvas[NSIG + 1]; extern unsigned __sighandflags[NSIG + 1]; extern const struct NtSecurityAttributes kNtIsInheritable; +void __fds_wipe(void); void __fds_lock(void); void __fds_unlock(void); -void __fds_funlock(void); #define __vforked (__tls_enabled && (__get_tls()->tib_flags & TIB_FLAG_VFORKED)) diff --git a/libc/calls/statfs.c b/libc/calls/statfs.c index 90b5bd69f..bd563ee6a 100644 --- a/libc/calls/statfs.c +++ b/libc/calls/statfs.c @@ -41,8 +41,12 @@ * @cancellationpoint */ int statfs(const char *path, struct statfs *sf) { - int rc; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" union statfs_meta m; + CheckLargeStackAllocation(&m, sizeof(m)); +#pragma GCC pop_options + int rc; struct ZiposUri zipname; BEGIN_CANCELLATION_POINT; diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 69aff8a4f..85dcd71c5 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -1,5 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ +#include "libc/nt/struct/overlapped.h" +#include "libc/thread/thread.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -25,7 +27,6 @@ COSMOPOLITAN_C_START_ struct Fd { char kind; - bool zombie; bool dontclose; char buflen; char buf[4]; @@ -33,6 +34,8 @@ struct Fd { unsigned mode; int64_t handle; int64_t extra; + int64_t pointer; + pthread_mutex_t lock; }; struct StdinRelay { @@ -42,6 +45,7 @@ struct StdinRelay { int64_t reader; /* ReadFile() use this instead */ int64_t writer; /* only used by WinStdinThread */ int64_t thread; /* handle for the stdio thread */ + struct NtOverlapped overlap; }; struct Fds { diff --git a/libc/calls/struct/sigaction.internal.h b/libc/calls/struct/sigaction.internal.h index d94fb3ada..c6bc2796d 100644 --- a/libc/calls/struct/sigaction.internal.h +++ b/libc/calls/struct/sigaction.internal.h @@ -64,11 +64,8 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *); #define DescribeSigaction(rc, sa) DescribeSigaction(alloca(256), rc, sa) void _init_onntconsoleevent(void); -void _init_wincrash(void); void _init_sigwinch(void); -void _init_sigchld(void); -extern intptr_t __sigchld_thread; extern intptr_t __sigwinch_thread; COSMOPOLITAN_C_END_ diff --git a/libc/calls/struct/siginfo-openbsd.internal.h b/libc/calls/struct/siginfo-openbsd.internal.h index f0a6322f8..dbe2074a5 100644 --- a/libc/calls/struct/siginfo-openbsd.internal.h +++ b/libc/calls/struct/siginfo-openbsd.internal.h @@ -12,21 +12,21 @@ struct siginfo_openbsd { int32_t _pad[(128 / 4) - 3]; struct { int32_t si_pid; + int32_t si_uid; union { struct { - int32_t si_uid; union sigval si_value; }; struct { int64_t si_utime; int64_t si_stime; - int32_t si_status; + int si_status; }; }; }; struct { void *si_addr; - int32_t si_trapno; + int si_trapno; }; }; }; diff --git a/libc/calls/struct/siginfo.h b/libc/calls/struct/siginfo.h index 2a90e7c13..50e2b4302 100644 --- a/libc/calls/struct/siginfo.h +++ b/libc/calls/struct/siginfo.h @@ -57,6 +57,11 @@ struct siginfo { typedef struct siginfo siginfo_t; +#ifdef _COSMO_SOURCE +void __minicrash(int, siginfo_t *, void *); +char __is_stack_overflow(siginfo_t *, void *); +#endif + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGINFO_H_ */ diff --git a/libc/calls/struct/sigset.h b/libc/calls/struct/sigset.h index afcace1b2..9a3569b85 100644 --- a/libc/calls/struct/sigset.h +++ b/libc/calls/struct/sigset.h @@ -16,7 +16,6 @@ int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull(); int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect; int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect; int sigcountset(const sigset_t *) paramsnonnull() nosideeffect; -int sigisprecious(int) nosideeffect; int sigprocmask(int, const sigset_t *, sigset_t *); int sigsuspend(const sigset_t *); int sigpending(sigset_t *); diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index dfd063392..7f01a5f70 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -31,6 +31,7 @@ #include "libc/nt/struct/luid.h" #include "libc/nt/struct/tokenprivileges.h" #include "libc/nt/thunk/msabi.h" +#include "libc/runtime/stack.h" #include "libc/sysv/errfuns.h" __msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; @@ -55,17 +56,23 @@ static textwindows void InitializeWinlink(void) { textwindows int sys_symlinkat_nt(const char *target, int newdirfd, const char *linkpath) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + struct { + char16_t target16[PATH_MAX]; + char16_t linkpath16[PATH_MAX]; + } M; + CheckLargeStackAllocation(&M, sizeof(M)); +#pragma GCC pop_options int targetlen; uint32_t attrs, flags; - char16_t target16[PATH_MAX]; - char16_t linkpath16[PATH_MAX]; // convert the paths - if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; - if ((targetlen = __mkntpath(target, target16)) == -1) return -1; + if (__mkntpathat(newdirfd, linkpath, 0, M.linkpath16) == -1) return -1; + if ((targetlen = __mkntpath(target, M.target16)) == -1) return -1; // determine if we need directory flag - if ((attrs = __imp_GetFileAttributesW(target16)) != -1u) { + if ((attrs = __imp_GetFileAttributesW(M.target16)) != -1u) { if (attrs & kNtFileAttributeDirectory) { flags = kNtSymbolicLinkFlagDirectory; } else { @@ -75,7 +82,7 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd, // win32 needs to know if it's a directory of a file symlink, but // that's hard to determine if we're creating a broken symlink so // we'll fall back to checking for trailing slash - if (targetlen && target16[targetlen - 1] == '\\') { + if (targetlen && M.target16[targetlen - 1] == '\\') { flags = kNtSymbolicLinkFlagDirectory; } else { flags = 0; @@ -90,9 +97,9 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd, } // we can now proceed - if (CreateSymbolicLink(linkpath16, target16, flags)) { + if (CreateSymbolicLink(M.linkpath16, M.target16, flags)) { return 0; } else { - return __fix_enotdir(-1, linkpath16); + return __fix_enotdir(-1, M.linkpath16); } } diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h index 649b56c92..763b107a7 100644 --- a/libc/calls/syscall_support-nt.internal.h +++ b/libc/calls/syscall_support-nt.internal.h @@ -12,7 +12,6 @@ char16_t *__create_pipe_name(char16_t *); int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]); int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int); int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]); -int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool); int ntaccesscheck(const char16_t *, uint32_t) paramsnonnull(); int sys_pause_nt(void); int64_t __fix_enotdir(int64_t, char16_t *); @@ -21,7 +20,6 @@ int64_t __winerr(void) nocallback privileged; int64_t ntreturn(uint32_t); void *GetProcAddressModule(const char *, const char *); void WinMainForked(void); -void _check_sigalrm(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/tcgetwinsize-nt.c b/libc/calls/tcgetwinsize-nt.c index 820497971..815b5fb71 100644 --- a/libc/calls/tcgetwinsize-nt.c +++ b/libc/calls/tcgetwinsize-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/winsize.internal.h" #include "libc/nt/console.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" @@ -35,11 +36,12 @@ textwindows int tcgetwinsize_nt(int fd, struct winsize *ws) { if (fd == STDIN_FILENO) { uint32_t dwMode; // WIN32 doesn't allow GetConsoleScreenBufferInfoEx(stdin) - if (GetConsoleMode(g_fds.p[STDIN_FILENO].handle, &dwMode)) { + if (g_fds.p[STDOUT_FILENO].kind != kFdEmpty && + GetConsoleMode(g_fds.p[STDOUT_FILENO].handle, &dwMode)) { hConsoleOutput = g_fds.p[STDOUT_FILENO].handle; - if (GetConsoleMode(hConsoleOutput, &dwMode)) { - hConsoleOutput = g_fds.p[STDERR_FILENO].handle; - } + } else if (g_fds.p[STDERR_FILENO].kind != kFdEmpty && + GetConsoleMode(g_fds.p[STDERR_FILENO].handle, &dwMode)) { + hConsoleOutput = g_fds.p[STDERR_FILENO].handle; } else { return enotty(); } diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index 3227039fc..75febe649 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -126,10 +126,12 @@ textwindows int __munge_terminal_input(char *p, uint32_t *n) { } } if (got_vintr) { - delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0); + __sig_raise(SIGINT, SI_KERNEL); + delivered |= true; } if (got_vquit) { - delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0); + __sig_raise(SIGQUIT, SI_KERNEL); + delivered |= true; } if (*n && !j) { if (delivered) { diff --git a/libc/calls/tinyprint.c b/libc/calls/tinyprint.c index 9540f730c..10e91810d 100644 --- a/libc/calls/tinyprint.c +++ b/libc/calls/tinyprint.c @@ -16,7 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/runtime/runtime.h" + +#define ASAN_ERROR "error: tinyprint() passed invalid memory, or missing NULL\n" static bool tinyflush(int fd, char *buf, size_t n, ssize_t *toto) { ssize_t rc; @@ -50,8 +56,13 @@ ssize_t tinyprint(int fd, const char *s, ...) { va_list va; ssize_t toto; char buf[512]; + BLOCK_CANCELLATIONS; va_start(va, s); for (toto = n = 0; s; s = va_arg(va, const char *)) { + if (IsAsan() && !__asan_is_valid_str(s)) { + write(2, ASAN_ERROR, sizeof(ASAN_ERROR) - 1); + abort(); + } while ((c = *s++)) { buf[n++] = c; if (n == sizeof(buf)) { @@ -64,5 +75,6 @@ ssize_t tinyprint(int fd, const char *s, ...) { } va_end(va); tinyflush(fd, buf, n, &toto); + ALLOW_CANCELLATIONS; return toto; } diff --git a/libc/calls/tkill.c b/libc/calls/tkill.c deleted file mode 100644 index ff6d4df0a..000000000 --- a/libc/calls/tkill.c +++ /dev/null @@ -1,161 +0,0 @@ -/*-*- 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/syscall-sysv.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/atomic.h" -#include "libc/intrin/dll.h" -#include "libc/intrin/strace.internal.h" -#include "libc/nt/enum/context.h" -#include "libc/nt/enum/threadaccess.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/context.h" -#include "libc/nt/thread.h" -#include "libc/runtime/syslib.internal.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/errfuns.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" - -static dontinline textwindows int __tkill_nt(int tid, int sig, - struct CosmoTib *tib) { - - // validate api usage - if (tid <= 0 || !(1 <= sig && sig <= 64)) { - return einval(); - } - - // check if caller is killing themself - if (tid == gettid() && (!tib || tib == __get_tls())) { - __sig_handle(0, sig, SI_TKILL, 0); - return 0; - } - - // check to see if this is a cosmo posix thread - struct Dll *e; - pthread_spin_lock(&_pthread_lock); - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { - enum PosixThreadStatus status; - struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); - int rhs = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (rhs <= 0 || tid != rhs) continue; - if (tib && tib != pt->tib) continue; - status = atomic_load_explicit(&pt->status, memory_order_acquire); - pthread_spin_unlock(&_pthread_lock); - if (status < kPosixThreadTerminated) { - if (pt->flags & PT_BLOCKED) { - pt->tib->tib_sigpending |= 1ull << (sig - 1); - return 0; - } else { - return _pthread_signal(pt, sig, SI_TKILL); - } - } else { - return 0; - } - } - pthread_spin_unlock(&_pthread_lock); - - // otherwise try our luck hunting a win32 thread - if (!tib) { - intptr_t h; - if ((h = OpenThread(kNtThreadTerminate, false, tid))) { - if (TerminateThread(h, sig)) { - return 0; - } else { - return __winerr(); - } - CloseHandle(h); - } else { - return __winerr(); - } - } else { - return esrch(); - } -} - -static int __tkill_posix(int tid, int sig, struct CosmoTib *tib) { - - // avoid lock when killing self - int me; - struct PosixThread *pt; - pt = (struct PosixThread *)__get_tls()->tib_pthread; - me = atomic_load_explicit(&__get_tls()->tib_tid, memory_order_relaxed); - if (tid == me && (!tib || tib == __get_tls())) { - return __syslib->pthread_kill(pt->next, sig); - } - - // otherwise look for matching thread - struct Dll *e; - pthread_spin_lock(&_pthread_lock); - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { - enum PosixThreadStatus status; - struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); - int rhs = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (rhs <= 0 || tid != rhs) continue; - if (tib && tib != pt->tib) continue; - status = atomic_load_explicit(&pt->status, memory_order_acquire); - pthread_spin_unlock(&_pthread_lock); - if (status < kPosixThreadTerminated) { - return __syslib->pthread_kill(pt->next, sig); - } else { - return 0; - } - } - pthread_spin_unlock(&_pthread_lock); - return esrch(); -} - -// OpenBSD has an optional `tib` parameter for extra safety. -int __tkill(int tid, int sig, void *tib) { - int rc; - if (IsXnuSilicon()) { - return __tkill_posix(tid, sig, tib); - } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { - rc = sys_tkill(tid, sig, tib); - } else if (IsWindows()) { - rc = __tkill_nt(tid, sig, tib); - } else { - rc = enosys(); - } - STRACE("tkill(%d, %G) → %d% m", tid, sig, rc); - return rc; -} - -/** - * Kills thread. - * - * @param tid is thread id - * @param sig does nothing on xnu - * @return 0 on success, or -1 w/ errno - * @raise EAGAIN if `RLIMIT_SIGPENDING` was exceeded - * @raise EINVAL if `tid` or `sig` were invalid - * @raise ESRCH if no such `tid` existed - * @raise EPERM if permission was denied - * @asyncsignalsafe - */ -int tkill(int tid, int sig) { - return __tkill(tid, sig, 0); -} diff --git a/libc/calls/ktmppath.c b/libc/calls/tmpdir.c similarity index 65% rename from libc/calls/ktmppath.c rename to libc/calls/tmpdir.c index 176ae681a..49f61d464 100644 --- a/libc/calls/ktmppath.c +++ b/libc/calls/tmpdir.c @@ -16,7 +16,9 @@ │ 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/cosmo.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/macros.internal.h" @@ -24,8 +26,63 @@ #include "libc/runtime/runtime.h" #include "libc/str/str.h" +static struct { + atomic_uint once; + union { + char path[PATH_MAX]; + char16_t path16[PATH_MAX / 2]; + }; +} __tmpdir; + +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +static void __tmpdir_init(void) { + int i; + char *s; + uint32_t n; + + if ((s = getenv("TMPDIR"))) { + if (*s != '/') { + if (!getcwd(__tmpdir.path, PATH_MAX)) { + goto GiveUp; + } + strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path)); + } + strlcat(__tmpdir.path, s, sizeof(__tmpdir.path)); + if (strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path)) < + PATH_MAX - NAME_MAX) { + return; + } + } + +GiveUp: + if (IsWindows() && + ((n = GetTempPath(ARRAYLEN(__tmpdir.path16), __tmpdir.path16)) && + n < ARRAYLEN(__tmpdir.path16))) { + // turn c:\foo\bar\ into c:/foo/bar/ + for (i = 0; i < n; ++i) { + if (__tmpdir.path16[i] == '\\') { + __tmpdir.path16[i] = '/'; + } + } + // turn c:/... into /c/... + if (IsAlpha(__tmpdir.path16[0]) && __tmpdir.path16[1] == ':' && + __tmpdir.path16[2] == '/') { + __tmpdir.path16[1] = __tmpdir.path16[0]; + __tmpdir.path16[0] = '/'; + __tmpdir.path16[2] = '/'; + } + tprecode16to8(__tmpdir.path, sizeof(__tmpdir.path), __tmpdir.path16); + return; + } + + strcpy(__tmpdir.path, "/tmp/"); +} + /** - * RII constant holding temporary file directory. + * Returns pretty good temporary directory. * * The order of precedence is: * @@ -33,52 +90,12 @@ * - GetTempPath(), for the New Technology * - /tmp/ to make security scene go crazy * - * This guarantees an absolute path with a trailing slash. We also - * ensure `kTmpPath` isn't longer than `PATH_MAX-NAME_MAX`. + * This guarantees an absolute path with a trailing slash. The returned + * value points to static memory with `PATH_MAX` bytes. The string will + * be short enough that at least `NAME_MAX` bytes remain. This function + * is thread safe so long as callers don't modified the returned memory */ -char kTmpPath[PATH_MAX]; - -static inline int IsAlpha(int c) { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); -} - -__attribute__((__constructor__)) static void kTmpPathInit(void) { - int i; - char *s; - uint32_t n; - char16_t path16[PATH_MAX]; - - if ((s = getenv("TMPDIR"))) { - if (*s != '/') { - if (!getcwd(kTmpPath, PATH_MAX)) { - goto GiveUp; - } - strlcat(kTmpPath, "/", sizeof(kTmpPath)); - } - strlcat(kTmpPath, s, sizeof(kTmpPath)); - if (strlcat(kTmpPath, "/", sizeof(kTmpPath)) < PATH_MAX - NAME_MAX) { - return; - } - } - -GiveUp: - if (IsWindows() && - ((n = GetTempPath(ARRAYLEN(path16), path16)) && n < ARRAYLEN(path16))) { - // turn c:\foo\bar\ into c:/foo/bar/ - for (i = 0; i < n; ++i) { - if (path16[i] == '\\') { - path16[i] = '/'; - } - } - // turn c:/... into /c/... - if (IsAlpha(path16[0]) && path16[1] == ':' && path16[2] == '/') { - path16[1] = path16[0]; - path16[0] = '/'; - path16[2] = '/'; - } - tprecode16to8(kTmpPath, sizeof(kTmpPath), path16); - return; - } - - strcpy(kTmpPath, "/tmp/"); +char *__get_tmpdir(void) { + cosmo_once(&__tmpdir.once, __tmpdir_init); + return __tmpdir.path; } diff --git a/libc/calls/tmpfd.c b/libc/calls/tmpfd.c index 657d36e7c..dc7b8620a 100644 --- a/libc/calls/tmpfd.c +++ b/libc/calls/tmpfd.c @@ -44,8 +44,7 @@ int _mkstemp(char *, int); * * This creates a secure temporary file inside $TMPDIR. If it isn't * defined, then /tmp is used on UNIX and GetTempPath() is used on the - * New Technology. This resolution of $TMPDIR happens once in a ctor, - * which is copied to the `kTmpPath` global. + * New Technology. This resolution of $TMPDIR happens once in a ctor. * * Once close() is called, the returned file is guaranteed to be deleted * automatically. On UNIX the file is unlink()'d before this function @@ -81,14 +80,14 @@ int tmpfd(void) { char path[PATH_MAX + 1]; if (IsLinux()) { e = errno; - if ((fd = open(kTmpPath, O_RDWR | O_TMPFILE_LINUX, 0600)) != -1) { + if ((fd = open(__get_tmpdir(), O_RDWR | O_TMPFILE_LINUX, 0600)) != -1) { return fd; } else { errno = e; } } path[0] = 0; - strlcat(path, kTmpPath, sizeof(path)); + strlcat(path, __get_tmpdir(), sizeof(path)); if (!(prog = program_invocation_short_name)) prog = "tmp"; strlcat(path, prog, sizeof(path)); strlcat(path, ".XXXXXX", sizeof(path)); diff --git a/libc/calls/ucontext.h b/libc/calls/ucontext.h index dae2192f5..b68541a2b 100644 --- a/libc/calls/ucontext.h +++ b/libc/calls/ucontext.h @@ -98,7 +98,7 @@ struct ucontext { uint8_t __unused[1024 / 8 - sizeof(sigset_t)]; struct sigcontext uc_mcontext; #endif -}; +} forcealign(16); typedef struct ucontext ucontext_t; diff --git a/libc/calls/uname.c b/libc/calls/uname.c index 56a3e3745..de964564b 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -75,15 +75,15 @@ static textwindows void GetNtName(char *name, int kind) { } } -static inline textwindows dontasan int GetNtMajorVersion(void) { +static inline textwindows int GetNtMajorVersion(void) { return NtGetPeb()->OSMajorVersion; } -static inline textwindows dontasan int GetNtMinorVersion(void) { +static inline textwindows int GetNtMinorVersion(void) { return NtGetPeb()->OSMinorVersion; } -static inline textwindows dontasan int GetNtBuildNumber(void) { +static inline textwindows int GetNtBuildNumber(void) { return NtGetPeb()->OSBuildNumber; } diff --git a/libc/calls/unveil.c b/libc/calls/unveil.c index a04d1fbfb..ca20f6f88 100644 --- a/libc/calls/unveil.c +++ b/libc/calls/unveil.c @@ -240,10 +240,8 @@ static char *JoinPaths(char *buf, size_t size, const char *path, } int sys_unveil_linux(const char *path, const char *permissions) { - int rc; - const char *dir; - const char *last; - const char *next; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" struct { char lbuf[PATH_MAX]; char buf1[PATH_MAX]; @@ -252,6 +250,11 @@ int sys_unveil_linux(const char *path, const char *permissions) { char buf4[PATH_MAX]; } b; CheckLargeStackAllocation(&b, sizeof(b)); +#pragma GCC pop_options + int rc; + const char *dir; + const char *last; + const char *next; if (!State.fd && (rc = unveil_init()) == -1) return rc; if ((path && !permissions) || (!path && permissions)) return einval(); diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c deleted file mode 100644 index cd5d9080a..000000000 --- a/libc/calls/wait4-nt.c +++ /dev/null @@ -1,191 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/assert.h" -#include "libc/calls/bo.internal.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/struct/rusage.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/fmt/conv.h" -#include "libc/intrin/strace.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/accounting.h" -#include "libc/nt/enum/accessmask.h" -#include "libc/nt/enum/processaccess.h" -#include "libc/nt/enum/status.h" -#include "libc/nt/enum/th32cs.h" -#include "libc/nt/enum/wait.h" -#include "libc/nt/process.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/filetime.h" -#include "libc/nt/struct/iocounters.h" -#include "libc/nt/struct/processentry32.h" -#include "libc/nt/struct/processmemorycounters.h" -#include "libc/nt/synchronization.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/lcg.internal.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/w.h" -#include "libc/sysv/errfuns.h" - -#ifdef __x86_64__ - -static textwindows void AddProcessStats(int64_t h, struct rusage *ru) { - struct NtProcessMemoryCountersEx memcount = { - .cb = sizeof(struct NtProcessMemoryCountersEx)}; - if (GetProcessMemoryInfo(h, &memcount, sizeof(memcount))) { - ru->ru_maxrss = MAX(ru->ru_maxrss, memcount.PeakWorkingSetSize / 1024); - ru->ru_majflt += memcount.PageFaultCount; - } else { - STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError()); - } - struct NtFileTime createfiletime, exitfiletime; - struct NtFileTime kernelfiletime, userfiletime; - if (GetProcessTimes(h, &createfiletime, &exitfiletime, &kernelfiletime, - &userfiletime)) { - ru->ru_utime = timeval_add( - ru->ru_utime, WindowsDurationToTimeVal(ReadFileTime(userfiletime))); - ru->ru_stime = timeval_add( - ru->ru_stime, WindowsDurationToTimeVal(ReadFileTime(kernelfiletime))); - } else { - STRACE("%s failed %u", "GetProcessTimes", GetLastError()); - } - struct NtIoCounters iocount; - if (GetProcessIoCounters(h, &iocount)) { - ru->ru_inblock += iocount.ReadOperationCount; - ru->ru_oublock += iocount.WriteOperationCount; - } else { - STRACE("%s failed %u", "GetProcessIoCounters", GetLastError()); - } -} - -static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus, - int options, - struct rusage *opt_out_rusage) { - int pids[64]; - int64_t handle; - uint32_t i, count; - int64_t handles[64]; - uint32_t dwExitCode; - if (*pid != -1 && *pid != 0) { - if (*pid < 0) { - // XXX: this is sloppy - *pid = -*pid; - } - if (!__isfdkind(*pid, kFdProcess)) { - // XXX: this is sloppy (see fork-nt.c) - if (!__isfdopen(*pid) && - (handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation, - true, *pid))) { - if ((*pid = __reservefd_unlocked(-1)) != -1) { - g_fds.p[*pid].kind = kFdProcess; - g_fds.p[*pid].handle = handle; - g_fds.p[*pid].flags = O_CLOEXEC; - } else { - CloseHandle(handle); - return echild(); - } - } else { - return echild(); - } - } - handles[0] = g_fds.p[*pid].handle; - pids[0] = *pid; - count = 1; - } else { - count = __sample_pids(pids, handles, false); - if (!count) { - return echild(); - } - } - - // wait for one of the processes to terminate - dwExitCode = kNtStillActive; - if (options & WNOHANG) { - i = WaitForMultipleObjects(count, handles, false, 0); - if (i == kNtWaitTimeout) { - return 0; - } - } else { - i = WaitForMultipleObjects(count, handles, false, - __SIG_POLLING_INTERVAL_MS); - if (i == kNtWaitTimeout) { - return -2; - } - } - if (i == kNtWaitFailed) { - notpossible; - } - - // WaitForMultipleObjects can say kNtWaitAbandoned which MSDN - // describes as a "sort of" successful status which indicates - // someone else didn't free a mutex and you should check that - // persistent resources haven't been left corrupted. not sure - // what those resources would be for process objects, however - // this status has actually been observed when waiting on 'em - i &= ~kNtWaitAbandoned; - - // this is where things get especially hairy. see exit() doc - if (!GetExitCodeProcess(handles[i], &dwExitCode)) { - notpossible; - } - if (dwExitCode == kNtStillActive) { - return -2; - } - if (dwExitCode == 0xc9af3d51u) { - dwExitCode = kNtStillActive; - } - - // now pass along the result - if (opt_out_wstatus) { - *opt_out_wstatus = dwExitCode; - } - if (opt_out_rusage) { - bzero(opt_out_rusage, sizeof(*opt_out_rusage)); - AddProcessStats(handles[i], opt_out_rusage); - } - CloseHandle(handles[i]); - __releasefd(pids[i]); - return pids[i]; -} - -textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, - struct rusage *opt_out_rusage) { - int rc; - sigset_t oldmask, mask = {0}; - sigaddset(&mask, SIGCHLD); - __sig_mask(SIG_BLOCK, &mask, &oldmask); - BEGIN_BLOCKING_OPERATION; - do { - rc = _check_interrupts(kSigOpRestartable | kSigOpNochld); - if (rc == -1) break; - __fds_lock(); - rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage); - __fds_unlock(); - } while (rc == -2); - END_BLOCKING_OPERATION; - __sig_mask(SIG_SETMASK, &oldmask, 0); - return rc; -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/wait4.h b/libc/calls/wait4.h deleted file mode 100644 index 5a532d225..000000000 --- a/libc/calls/wait4.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_WAIT4_H_ -#define COSMOPOLITAN_LIBC_CALLS_WAIT4_H_ -#include "libc/calls/struct/rusage.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -int sys_wait4_nt(int, int *, int, struct rusage *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_WAIT4_H_ */ diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c deleted file mode 100644 index 3ae800eab..000000000 --- a/libc/calls/wincrash.c +++ /dev/null @@ -1,129 +0,0 @@ -/*-*- 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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/intrin/describebacktrace.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/nt/enum/exceptionhandleractions.h" -#include "libc/nt/enum/signal.h" -#include "libc/nt/enum/status.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/ntexceptionpointers.h" -#include "libc/nt/thunk/msabi.h" -#include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/consts/sig.h" - -#ifdef __x86_64__ - -// win32 calls this; we're running inside the thread that crashed -__msabi unsigned __wincrash(struct NtExceptionPointers *ep) { - int sig, code; - - switch (ep->ExceptionRecord->ExceptionCode) { - case kNtSignalBreakpoint: - code = TRAP_BRKPT; - sig = SIGTRAP; - // Windows seems to be the only operating system that traps INT3 at - // addressof(INT3) rather than addressof(INT3)+1. So we must adjust - // RIP to prevent the same INT3 from being trapped forevermore. - ep->ContextRecord->Rip++; - break; - case kNtSignalIllegalInstruction: - code = ILL_ILLOPC; - sig = SIGILL; - break; - case kNtSignalPrivInstruction: - code = ILL_PRVOPC; - sig = SIGILL; - break; - case kNtSignalGuardPage: - case kNtSignalInPageError: - case kNtStatusStackOverflow: - code = SEGV_MAPERR; - sig = SIGSEGV; - break; - case kNtSignalAccessViolation: - code = SEGV_ACCERR; - sig = SIGSEGV; - break; - case kNtSignalInvalidHandle: - case kNtSignalInvalidParameter: - case kNtSignalAssertionFailure: - code = SI_USER; - sig = SIGABRT; - break; - case kNtStatusIntegerOverflow: - code = FPE_INTOVF; - sig = SIGFPE; - break; - case kNtSignalFltDivideByZero: - code = FPE_FLTDIV; - sig = SIGFPE; - break; - case kNtSignalFltOverflow: - code = FPE_FLTOVF; - sig = SIGFPE; - break; - case kNtSignalFltUnderflow: - code = FPE_FLTUND; - sig = SIGFPE; - break; - case kNtSignalFltInexactResult: - code = FPE_FLTRES; - sig = SIGFPE; - break; - case kNtSignalFltDenormalOperand: - case kNtSignalFltInvalidOperation: - case kNtSignalFltStackCheck: - case kNtSignalIntegerDivideByZero: - case kNtSignalFloatMultipleFaults: - case kNtSignalFloatMultipleTraps: - code = FPE_FLTINV; - sig = SIGFPE; - break; - case kNtSignalDllNotFound: - case kNtSignalOrdinalNotFound: - case kNtSignalEntrypointNotFound: - case kNtSignalDllInitFailed: - code = SI_KERNEL; - sig = SIGSYS; - break; - default: - code = ep->ExceptionRecord->ExceptionCode; - sig = SIGSEGV; - break; - } - - STRACE("win32 vectored exception raising 0x%08Xu %G rip %x bt %s", - ep->ExceptionRecord->ExceptionCode, sig, ep->ContextRecord->Rip, - DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp)); - - if (__sighandflags[sig] & SA_SIGINFO) { - struct Delivery pkg = {kSigOpUnmaskable, sig, code, ep->ContextRecord}; - __sig_tramp(&pkg); - } else { - __sig_handle(kSigOpUnmaskable, sig, code, 0); - } - - return kNtExceptionContinueExecution; -} - -#endif /* __x86_64__ */ diff --git a/libc/calls/wincrash_init.S b/libc/calls/wincrash_init.S deleted file mode 100644 index bbf659738..000000000 --- a/libc/calls/wincrash_init.S +++ /dev/null @@ -1,32 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/macros.internal.h" - - .init.start 300,_init_wincrash - testb IsWindows() - jz 1f -#if !IsTiny() - mov __wincrashearly(%rip),%rcx - ntcall __imp_RemoveVectoredExceptionHandler -#endif - pushpop 1,%rcx - ezlea __wincrash,dx - ntcall __imp_AddVectoredExceptionHandler -1: .init.end 300,_init_wincrash,globl,hidden diff --git a/libc/calls/winstdin1.c b/libc/calls/winstdin1.c index 0894737b6..74011bd90 100644 --- a/libc/calls/winstdin1.c +++ b/libc/calls/winstdin1.c @@ -16,98 +16,38 @@ │ 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/syscall_support-nt.internal.h" #include "libc/dce.h" -#include "libc/fmt/itoa.h" -#include "libc/intrin/atomic.h" -#include "libc/intrin/strace.internal.h" -#include "libc/log/libfatal.internal.h" #include "libc/nt/console.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" #include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/ipc.h" #include "libc/nt/runtime.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" -#include "libc/nt/thunk/msabi.h" #include "libc/thread/tls.h" -#include "libc/thread/tls2.internal.h" -/** - * @fileoverview makes windows stdin handle capable of being poll'd - * - * 1. On Windows, there's no way to check how many bytes of input are - * available from the cmd.exe console. The only thing you can do is a - * blocking read that can't be interrupted. - * - * 2. On Windows, it's up to the parent process whether or not the - * handles it passes us are capable of non-blocking overlapped i/o - * reads (which we need for busy polling to check for interrupts). - * - * So we solve this by creating a thread which just does naive reads on - * standard input, and then relays the data to the process via a named - * pipe, which we explicitly creaete with overlapped i/o enabled. - * - * This code runs very early during process initialization, at the - * beginning of WinMain(). This module is only activated if the app - * links any one of: poll(), select(), or ioctl(FIONREAD). - */ - -__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; -__msabi extern typeof(CreateFile) *const __imp_CreateFileW; -__msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW; -__msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW; -__msabi extern typeof(CreateThread) *const __imp_CreateThread; -__msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode; -__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; -__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; -__msabi extern typeof(ReadFile) *const __imp_ReadFile; -__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; -__msabi extern typeof(WriteFile) *const __imp_WriteFile; - -static unsigned long StrLen(const char *s) { - unsigned long n = 0; - while (*s++) ++n; - return n; -} - -static void Log(const char *s) { -#if 0 - __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); -#endif -} - -__msabi static textwindows uint32_t WinStdinThread(void *lpParameter) { +static textwindows dontinstrument wontreturn void WinStdinThread(void) { char buf[4]; + struct CosmoTib tls; uint32_t i, got, wrote; - Log(" activated thread\n"); + __bootstrap_tls(&tls, __builtin_frame_address(0)); for (;;) { - Log(" wait\n"); - __imp_WaitForSingleObject(g_fds.stdin.inisem, -1u); - Log(" read\n"); - if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) { - Log(" read failed\n"); - goto Finish; - } - if (!got) { - Log(" eof\n"); - goto Finish; - } + WaitForSingleObject(g_fds.stdin.inisem, -1u); + if (!ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) goto Finish; + if (!got) goto Finish; for (i = 0; i < got; i += wrote) { - Log(" write"); - if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { - Log(" write failed\n"); + if (!WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { goto Finish; } } } Finish: - __imp_CloseHandle(g_fds.stdin.writer); - return 0; + CloseHandle(g_fds.stdin.writer); + ExitThread(0); } textwindows static char16_t *FixCpy(char16_t p[17], uint64_t x, uint8_t k) { @@ -125,59 +65,27 @@ textwindows static char16_t *CreateStdinPipeName(char16_t *a, int64_t h) { return a; } -// this makes it possible for our read() implementation to periodically -// poll for signals while performing a blocking overlapped io operation textwindows void WinMainStdin(void) { uint32_t conmode; char16_t pipename[64]; int64_t hStdin, hWriter, hReader, hThread, hSemaphore; if (!SupportsWindows()) return; - // handle numbers stay the same when inherited by the subprocesses - hStdin = __imp_GetStdHandle(kNtStdInputHandle); - if (hStdin == kNtInvalidHandleValue) { - Log(" GetStdHandle failed\n"); - return; - } - if (!__imp_GetConsoleMode(hStdin, &conmode)) { - Log(" stdin not a console\n"); - return; - } + hStdin = GetStdHandle(kNtStdInputHandle); + if (!GetConsoleMode(hStdin, &conmode)) return; CreateStdinPipeName(pipename, hStdin); - hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting, - kNtFileFlagOverlapped, 0); - if (hReader == kNtInvalidHandleValue) { - // we are the init process; create pipe server to dole out stdin - hWriter = __imp_CreateNamedPipeW( - pipename, kNtPipeAccessOutbound | kNtFileFlagOverlapped, - kNtPipeTypeMessage | kNtPipeReadmodeMessage | kNtPipeNowait, - kNtPipeUnlimitedInstances, 4096, 4096, 0, 0); - if (hWriter == kNtInvalidHandleValue) { - Log(" CreateNamedPipe failed\n"); - return; - } - hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting, - kNtFileFlagOverlapped, 0); - if (hReader == kNtInvalidHandleValue) { - Log(" CreateFile failed\n"); - return; - } - // create non-inherited semaphore with initial value of 0 - hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); - if (!hSemaphore) { - Log(" CreateSemaphore failed\n"); - return; - } - hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); - if (!hThread) { - Log(" CreateThread failed\n"); - return; - } - } else { - // we are the child of a cosmo process with its own stdin thread - hWriter = 0; - hThread = 0; - hSemaphore = 0; - } + hWriter = CreateNamedPipe( + pipename, kNtPipeAccessOutbound | kNtFileFlagOverlapped, + kNtPipeTypeMessage | kNtPipeReadmodeMessage | kNtPipeNowait, + kNtPipeUnlimitedInstances, 4096, 4096, 0, 0); + if (hWriter == kNtInvalidHandleValue) return; + hReader = CreateFile(pipename, kNtGenericRead, 0, 0, kNtOpenExisting, + kNtFileFlagOverlapped, 0); + if (hReader == kNtInvalidHandleValue) return; + hSemaphore = CreateSemaphore(0, 0, 1, 0); + if (!hSemaphore) return; + hThread = CreateThread(0, 65536, (void *)WinStdinThread, 0, + kNtStackSizeParamIsAReservation, 0); + if (!hThread) return; g_fds.stdin.handle = hStdin; g_fds.stdin.thread = hThread; g_fds.stdin.reader = hReader; diff --git a/libc/calls/winthreadlaunch.S b/libc/calls/winthreadlaunch.S deleted file mode 100644 index b68b8386f..000000000 --- a/libc/calls/winthreadlaunch.S +++ /dev/null @@ -1,47 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ 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/macros.internal.h" -.text.windows - -// Used by clone() on Windows to launch thread. -// -// Windows owns the stack memory when we initially enter threads. -// This function switches us over, so that we can start using the -// runtime facilities. -// -// @param %rdi is arg1 -// @param %rsi is arg2 -// @param %rdx is func -// @param %rcx is stack -// @return %rax is res -// @see clone() -WinThreadLaunch: - push %rbx - push %r15 - mov %rbp,%r15 - mov %rsp,%rbx - mov %rcx,%rsp - xor %rbp,%rbp - call *%rdx - mov %r15,%rbp - mov %rbx,%rsp - pop %r15 - pop %rbx - ret - .endfn WinThreadLaunch,globl,hidden diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 681885552..26d38b8cc 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -16,10 +16,10 @@ │ 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/sig.internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" @@ -27,57 +27,107 @@ #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/nt/enum/filetype.h" +#include "libc/nt/enum/wait.h" #include "libc/nt/errors.h" +#include "libc/nt/events.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/overlapped.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/runtime/runtime.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/thread.h" +#include "libc/thread/tls.h" + +__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, ssize_t offset) { // perform the write i/o operation bool32 ok; + struct Fd *f; uint32_t sent; int64_t handle; + struct PosixThread *pt; - if (g_fds.p[fd].kind == kFdConsole) { - handle = g_fds.p[fd].extra; // get write end of console - } else { - handle = g_fds.p[fd].handle; - } - - // don't use pread() or pwrite() on a pipe - if (offset != -1) { - uint32_t filetype = GetFileType(handle); - if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) { - return espipe(); - } - } - + f = g_fds.p + fd; + pt = _pthread_self(); size = MIN(size, 0x7ffff000); - if (offset == -1) { - // perform simple blocking write - ok = WriteFile(handle, data, size, &sent, 0); + if (f->kind == kFdConsole) { + handle = f->extra; // get write end of console } else { - // perform pwrite()-style write at particular file offset - int64_t position; - // save file pointer which windows clobbers, even for overlapped i/o - if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) { - return __winerr(); // fd probably isn't seekable? - } - struct NtOverlapped overlap = {0}; - overlap.Pointer = (void *)(uintptr_t)offset; - ok = WriteFile(handle, data, size, 0, &overlap); - if (!ok && GetLastError() == kNtErrorIoPending) ok = true; - if (ok) ok = GetOverlappedResult(handle, &overlap, &sent, true); - // restore file pointer which windows clobbers, even on error - unassert(SetFilePointerEx(handle, position, 0, SEEK_SET)); + handle = f->handle; } + + bool pwriting = offset != -1; + bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk; + bool nonblock = !!(f->flags & O_NONBLOCK); + pt->abort_errno = EAGAIN; + + if (pwriting && !seekable) { + return espipe(); + } + if (!pwriting) { + offset = 0; + } + if (seekable && !pwriting) { + pthread_mutex_lock(&f->lock); + offset = f->pointer; + } + + struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0), + .Pointer = offset}; + ok = WriteFile(handle, data, size, 0, &overlap); + if (!ok && GetLastError() == kNtErrorIoPending) { + BlockingOperation: + if (!nonblock) { + pt->ioverlap = &overlap; + pt->iohandle = handle; + } + if (nonblock) { + CancelIoEx(handle, &overlap); + } else if (_check_interrupts(kSigOpRestartable)) { + Interrupted: + pt->abort_errno = errno; + CancelIoEx(handle, &overlap); + } else { + for (;;) { + uint32_t i; + i = WaitForSingleObject(overlap.hEvent, __SIG_IO_INTERVAL_MS); + if (i == kNtWaitTimeout) { + if (_check_interrupts(kSigOpRestartable)) { + goto Interrupted; + } + } else { + break; + } + } + } + pt->ioverlap = 0; + pt->iohandle = 0; + ok = true; + } + if (ok) { + // overlapped is allocated on stack, so it's important we wait + // for windows to acknowledge that it's done using that memory + ok = GetOverlappedResult(handle, &overlap, &sent, nonblock); + if (!ok && GetLastError() == kNtErrorIoIncomplete) { + goto BlockingOperation; + } + } + __imp_CloseHandle(overlap.hEvent); // __imp_ to avoid log noise + + if (seekable && !pwriting) { + if (ok) f->pointer = offset + sent; + pthread_mutex_unlock(&f->lock); + } + if (ok) { return sent; } @@ -89,15 +139,18 @@ 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 - if (_weaken(__sig_handle)) { - _weaken(__sig_handle)(0, SIGPIPE, SI_KERNEL, 0); + if (_weaken(__sig_raise)) { + _weaken(__sig_raise)(SIGPIPE, SI_KERNEL); return epipe(); } else { STRACE("broken pipe"); - ExitProcess(SIGPIPE); + TerminateThisProcess(SIGPIPE); } case kNtErrorAccessDenied: // write doesn't return EACCESS return ebadf(); + case kNtErrorOperationAborted: + errno = pt->abort_errno; + return -1; default: return __winerr(); } @@ -113,7 +166,13 @@ textwindows ssize_t sys_write_nt(int fd, const struct iovec *iov, size_t iovlen, for (total = i = 0; i < iovlen; ++i) { if (!iov[i].iov_len) continue; rc = sys_write_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset); - if (rc == -1) return -1; + if (rc == -1) { + if (total && errno != ECANCELED) { + return total; + } else { + return -1; + } + } total += rc; if (opt_offset != -1) opt_offset += rc; if (rc < iov[i].iov_len) break; diff --git a/libc/calls/write.c b/libc/calls/write.c index e32aaa62e..6a18a9811 100644 --- a/libc/calls/write.c +++ b/libc/calls/write.c @@ -70,7 +70,7 @@ ssize_t write(int fd, const void *buf, size_t size) { if (fd < 0) { rc = ebadf(); - } else if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) { + } else if (IsAsan() && !__asan_is_valid(buf, size)) { rc = efault(); } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { rc = ebadf(); // posix specifies this when not open()'d for writing diff --git a/libc/elf/elf.mk b/libc/elf/elf.mk index 2d4190461..e61f619f2 100644 --- a/libc/elf/elf.mk +++ b/libc/elf/elf.mk @@ -39,6 +39,12 @@ $(LIBC_ELF_A).pkg: \ $(LIBC_ELF_A_OBJS) \ $(foreach x,$(LIBC_ELF_A_DIRECTDEPS),$($(x)_A).pkg) +$(LIBC_ELF_A_OBJS): private \ + COPTS += \ + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + LIBC_ELF_LIBS = $(foreach x,$(LIBC_ELF_ARTIFACTS),$($(x))) LIBC_ELF_SRCS = $(foreach x,$(LIBC_ELF_ARTIFACTS),$($(x)_SRCS)) LIBC_ELF_HDRS = $(foreach x,$(LIBC_ELF_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/errno.h b/libc/errno.h index bf0342b9f..13581ff8a 100644 --- a/libc/errno.h +++ b/libc/errno.h @@ -28,11 +28,11 @@ COSMOPOLITAN_C_START_ /* this header is included by 700+ files; therefore we */ /* hand-roll &__get_tls()->tib_errno to avoid #include */ /* cosmopolitan uses x28 as the tls register b/c apple */ -#define errno \ - (*({ \ - errno_t *__ep; \ - asm("sub\t%0,x28,#1092" : "=r"(__ep)); \ - __ep; \ +#define errno \ + (*({ \ + errno_t *__ep; \ + asm("sub\t%0,x28,#68" : "=r"(__ep)); \ + __ep; \ })) #else #define errno (*__errno_location()) diff --git a/libc/fmt/fmt.mk b/libc/fmt/fmt.mk index 2c787c60d..77106ecb4 100644 --- a/libc/fmt/fmt.mk +++ b/libc/fmt/fmt.mk @@ -81,10 +81,11 @@ o/$(MODE)/libc/fmt/wcstoumax.o: private \ o/$(MODE)/libc/fmt/strerrno.greg.o \ o/$(MODE)/libc/fmt/strerrdoc.greg.o \ o/$(MODE)/libc/fmt/strerror_wr.greg.o: private \ - CFLAGS += \ + COPTS += \ -fpie \ - -ffreestanding \ - $(NO_MAGIC) + -fno-sanitize=all \ + -fno-stack-protector \ + -fpatchable-function-entry=0,0 LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x))) LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS)) diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 0cf16d802..d3e5db522 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -26,6 +26,7 @@ #include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" #include "libc/intrin/cmpxchg.h" +#include "libc/intrin/describebacktrace.internal.h" #include "libc/intrin/directmap.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/leaky.internal.h" @@ -830,12 +831,17 @@ static void __asan_report_memory_origin(const unsigned char *addr, int size, static __wur __asan_die_f *__asan_report(const void *addr, int size, const char *message, signed char kind) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + char buf[8192]; + CheckLargeStackAllocation(buf, sizeof(buf)); +#pragma GCC pop_options int i; wint_t c; signed char t; uint64_t x, y, z; + char *base, *q, *p = buf; struct MemoryIntervals *m; - char buf[8192], *base, *q, *p = buf; ftrace_enabled(-1); kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n", __asan_describe_access_poison(kind), size, message, addr, @@ -1389,7 +1395,8 @@ void __asan_map_shadow(uintptr_t p, size_t n) { struct DirectMap sm; struct MemoryIntervals *m; if (OverlapsShadowSpace((void *)p, n)) { - kprintf("error: %p size %'zu overlaps shadow space\n", p, n); + kprintf("error: %p size %'zu overlaps shadow space: %s\n", p, n, + DescribeBacktrace(__builtin_frame_address(0))); _Exit(1); } m = &_mmi; @@ -1423,12 +1430,6 @@ void __asan_map_shadow(uintptr_t p, size_t n) { __asan_unpoison((char *)p, n); } -static size_t __asan_strlen(const char *s) { - size_t i = 0; - while (s[i]) ++i; - return i; -} - static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m, size_t i) { uintptr_t x, y; @@ -1440,87 +1441,22 @@ static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m, } } -static textstartup char *__asan_get_last_string(char **list) { - char *res = 0; - for (int i = 0; list[i]; ++i) { - res = list[i]; - } - return res; -} - -static textstartup uintptr_t __asan_get_stack_top(int argc, char **argv, - char **envp, - unsigned long *auxv, - long pagesz) { - uintptr_t top; - const char *s; - if ((s = __asan_get_last_string(envp)) || - (s = __asan_get_last_string(argv))) { - top = (uintptr_t)s + __asan_strlen(s); - } else { - unsigned long *xp = auxv; - while (*xp) xp += 2; - top = (uintptr_t)xp; - } - return (top + (pagesz - 1)) & -pagesz; -} - -static textstartup void __asan_shadow_existing_mappings(int argc, char **argv, - char **envp, - unsigned long *auxv) { +static textstartup void __asan_shadow_existing_mappings(void) { __asan_shadow_mapping(&_mmi, 0); - - // WinMain() maps its own stack and its shadow too - if (IsWindows()) { - return; + if (!IsWindows()) { + int guard; + void *addr; + size_t size; + __get_main_stack(&addr, &size, &guard); + __asan_map_shadow((uintptr_t)addr, size); + __asan_poison(addr, guard, kAsanStackOverflow); } +} - // get microprocessor page size - long pagesz = 4096; - for (int i = 0; auxv[i]; i += 2) { - if (auxv[i] == AT_PAGESZ) { - pagesz = auxv[i + 1]; - } - } - - // get configured stack size of main thread - // supported platforms use defaults ranging from 4mb to 512mb - struct rlimit rlim; - uintptr_t stack_size = 4 * 1024 * 1024; - if (!sys_getrlimit(RLIMIT_STACK, &rlim) && // - rlim.rlim_cur > 0 && rlim.rlim_cur < RLIM_INFINITY) { - stack_size = (rlim.rlim_cur + (pagesz - 1)) & -pagesz; - } - - // Every UNIX system in our support vector creates arg blocks like: - // - // - // last environ string - // ... - // first environ string - // ... - // auxiliary value pointers - // environ pointers - // argument pointers - // argument count - // --- %rsp _start() - // ... - // ... - // ... program's stack - // ... - // ... - // - // - // The region of memory between highest and lowest can be computed - // across all supported platforms ±1 page accuracy as the distance - // between the last character of the last environ variable rounded - // up to the microprocessor page size (this computes the top addr) - // and the bottom is computed by subtracting RLIMIT_STACK rlim_cur - // It's simple but gets tricky if we consider environ can be empty - uintptr_t stack_top = __asan_get_stack_top(argc, argv, envp, auxv, pagesz); - uintptr_t stack_bot = stack_top - stack_size; - __asan_map_shadow(stack_bot, stack_top - stack_bot); - __asan_poison((void *)stack_bot, GetGuardSize(), kAsanStackOverflow); +static size_t __asan_strlen(const char *s) { + size_t i = 0; + while (s[i]) ++i; + return i; } forceinline ssize_t __write_str(const char *s) { @@ -1541,7 +1477,7 @@ void __asan_init(int argc, char **argv, char **envp, unsigned long *auxv) { REQUIRE(dlmemalign); REQUIRE(dlmalloc_usable_size); } - __asan_shadow_existing_mappings(argc, argv, envp, auxv); + __asan_shadow_existing_mappings(); __asan_map_shadow((uintptr_t)__executable_start, _end - __executable_start); __asan_map_shadow(0, 4096); __asan_poison((void *)__veil("r", 0L), getauxval(AT_PAGESZ), kAsanNullPage); diff --git a/libc/intrin/cosmo_once.c b/libc/intrin/cosmo_once.c index a4efb5825..b8eb51c60 100644 --- a/libc/intrin/cosmo_once.c +++ b/libc/intrin/cosmo_once.c @@ -38,7 +38,7 @@ */ errno_t cosmo_once(atomic_uint *once, void init(void)) { uint32_t old; - switch ((old = atomic_load_explicit(once, memory_order_relaxed))) { + switch ((old = atomic_load_explicit(once, memory_order_acquire))) { case INIT: if (atomic_compare_exchange_strong_explicit(once, &old, CALLING, memory_order_acquire, diff --git a/libc/intrin/cp.c b/libc/intrin/cp.c index 9e11a916f..7d34f347b 100644 --- a/libc/intrin/cp.c +++ b/libc/intrin/cp.c @@ -31,8 +31,8 @@ int begin_cancellation_point(void) { if (__tls_enabled) { tib = __get_tls(); if ((pt = (struct PosixThread *)tib->tib_pthread)) { - state = pt->flags & PT_INCANCEL; - pt->flags |= PT_INCANCEL; + state = pt->pt_flags & PT_INCANCEL; + pt->pt_flags |= PT_INCANCEL; } } return state; @@ -44,8 +44,8 @@ void end_cancellation_point(int state) { if (__tls_enabled) { tib = __get_tls(); if ((pt = (struct PosixThread *)tib->tib_pthread)) { - pt->flags &= ~PT_INCANCEL; - pt->flags |= state; + pt->pt_flags &= ~PT_INCANCEL; + pt->pt_flags |= state; } } } diff --git a/libc/intrin/createfile.c b/libc/intrin/createfile.c index 944584801..7a0db6195 100644 --- a/libc/intrin/createfile.c +++ b/libc/intrin/createfile.c @@ -56,7 +56,7 @@ TryAgain: switch (__imp_GetLastError()) { case kNtErrorPipeBusy: if (micros >= 1024) __imp_Sleep(micros / 1024); - if (micros / 1024 < __SIG_POLLING_INTERVAL_MS) micros <<= 1; + if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1; goto TryAgain; case kNtErrorAccessDenied: // GetNtOpenFlags() always greedily requests execute permissions diff --git a/libc/intrin/createfilemapping.c b/libc/intrin/createfilemapping.c index abe217c2d..0bf7f172b 100644 --- a/libc/intrin/createfilemapping.c +++ b/libc/intrin/createfilemapping.c @@ -43,12 +43,10 @@ textwindows int64_t CreateFileMapping( flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, opt_lpName); if (!hHandle) __winerr(); -#if 1 NTTRACE("CreateFileMapping(%ld, %s, %s, %'zu, %#hs) → %ld% m", opt_hFile, DescribeNtSecurityAttributes(opt_lpFileMappingAttributes), DescribeNtPageFlags(flProtect), (uint64_t)dwMaximumSizeHigh << 32 | dwMaximumSizeLow, opt_lpName, hHandle); -#endif return hHandle; } diff --git a/libc/intrin/createnamedpipe.c b/libc/intrin/createnamedpipe.c index 3a0bfdabe..aec9b2e03 100644 --- a/libc/intrin/createnamedpipe.c +++ b/libc/intrin/createnamedpipe.c @@ -50,7 +50,7 @@ TryAgain: nDefaultTimeOutMs, opt_lpSecurityAttributes); if (hServer == -1 && __imp_GetLastError() == kNtErrorPipeBusy) { if (micros >= 1024) __imp_Sleep(micros / 1024); - if (micros / 1024 < __SIG_POLLING_INTERVAL_MS) micros <<= 1; + if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1; goto TryAgain; } if (hServer == -1) __winerr(); diff --git a/libc/intrin/createthread.c b/libc/intrin/createthread.c index 51a7de782..d79cfe989 100644 --- a/libc/intrin/createthread.c +++ b/libc/intrin/createthread.c @@ -28,6 +28,9 @@ __msabi extern typeof(CreateThread) *const __imp_CreateThread; * Opens file on the New Technology. * * @param dwStackSize may be 0 for default per executable + * @param dwCreationFlags is a bitmask that may consist of: + * - kNtCreateSuspended + * - kNtStackSizeParamIsAReservation * @return thread handle, or 0 on failure * @note this wrapper takes care of ABI, STRACE() */ @@ -38,9 +41,9 @@ CreateThread(const struct NtSecurityAttributes *lpThreadAttributes, int64_t hHandle; hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId); - NTTRACE("CreateThread(%s, %'zu, %p, %p, %s, %p) → %ld% m", + NTTRACE("CreateThread(%s, %'zu, %t, %p, %s, %p) → %ld% m", DescribeNtSecurityAttributes(lpThreadAttributes), dwStackSize, - lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId, - hHandle); + lpStartAddress, lpParameter, + DescribeThreadCreateFlags(dwCreationFlags), opt_lpThreadId, hHandle); return hHandle; } diff --git a/libc/intrin/cxaatexit.c b/libc/intrin/cxaatexit.c index be2f8d014..fcc809096 100644 --- a/libc/intrin/cxaatexit.c +++ b/libc/intrin/cxaatexit.c @@ -42,7 +42,7 @@ __static_yoink("__cxa_finalize"); * @return 0 on success or nonzero w/ errno * @note folks have forked libc in past just to unbloat atexit() */ -dontasan int __cxa_atexit(void *fp, void *arg, void *pred) { +int __cxa_atexit(void *fp, void *arg, void *pred) { /* asan runtime depends on this function */ unsigned i; struct CxaAtexitBlock *b, *b2; diff --git a/libc/intrin/cxalock.c b/libc/intrin/cxalock.c index c1fde353d..632698675 100644 --- a/libc/intrin/cxalock.c +++ b/libc/intrin/cxalock.c @@ -21,18 +21,19 @@ static pthread_mutex_t __cxa_lock_obj; -void(__cxa_lock)(void) { - pthread_mutex_lock(&__cxa_lock_obj); -} - -void(__cxa_unlock)(void) { - pthread_mutex_unlock(&__cxa_lock_obj); -} - -void(__cxa_funlock)(void) { +void __cxa_wipe(void) { pthread_mutex_init(&__cxa_lock_obj, 0); } -__attribute__((__constructor__)) static void __cxa_init(void) { - pthread_atfork(__cxa_lock, __cxa_unlock, __cxa_funlock); +void __cxa_lock(void) { + pthread_mutex_lock(&__cxa_lock_obj); +} + +void __cxa_unlock(void) { + pthread_mutex_unlock(&__cxa_lock_obj); +} + +__attribute__((__constructor__)) static void __cxa_init(void) { + __cxa_wipe(); + pthread_atfork(__cxa_lock, __cxa_unlock, __cxa_wipe); } diff --git a/libc/intrin/describebacktrace.c b/libc/intrin/describebacktrace.c index fbf70a66f..87183e87f 100644 --- a/libc/intrin/describebacktrace.c +++ b/libc/intrin/describebacktrace.c @@ -17,14 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/describebacktrace.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/log/libfatal.internal.h" #include "libc/nexgen32e/stackframe.h" #define N 100 -#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__) - dontinstrument const char *(DescribeBacktrace)(char buf[N], struct StackFrame *fr) { bool gotsome = false; @@ -44,5 +41,6 @@ dontinstrument const char *(DescribeBacktrace)(char buf[N], } fr = fr->next; } + *p = 0; return buf; } diff --git a/libc/intrin/describecancelstate.c b/libc/intrin/describecancelstate.c new file mode 100644 index 000000000..ef78f3ebd --- /dev/null +++ b/libc/intrin/describecancelstate.c @@ -0,0 +1,31 @@ +/*-*- 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/fmt/itoa.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/thread/thread.h" + +const char *(DescribeCancelState)(char buf[12], int err, int *state) { + if (err) return "n/a"; + if (!state) return "NULL"; + if (*state == PTHREAD_CANCEL_ENABLE) return "PTHREAD_CANCEL_ENABLE"; + if (*state == PTHREAD_CANCEL_DISABLE) return "PTHREAD_CANCEL_DISABLE"; + if (*state == PTHREAD_CANCEL_MASKED) return "PTHREAD_CANCEL_MASKED"; + FormatInt32(buf, *state); + return buf; +} diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index b30a5d255..7b8e3fd2e 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -13,6 +13,7 @@ const char *DescribeFlags(char *, size_t, const struct DescribeFlags *, size_t, const char *, unsigned); const char *DescribeArchPrctlCode(char[12], int); +const char *DescribeCancelState(char[12], int, int *); const char *DescribeCapability(char[32], int); const char *DescribeClockName(char[32], int); const char *DescribeDirfd(char[12], int); @@ -43,6 +44,7 @@ const char *DescribeNtPipeOpenFlags(char[64], uint32_t); const char *DescribeNtProcAccessFlags(char[256], uint32_t); const char *DescribeNtStartFlags(char[128], uint32_t); const char *DescribeNtSymlinkFlags(char[64], uint32_t); +const char *DescribeThreadCreateFlags(char[64], uint32_t); const char *DescribeOpenFlags(char[128], int); const char *DescribeOpenMode(char[15], int, int); const char *DescribePersonalityFlags(char[128], int); @@ -68,6 +70,7 @@ const char *DescribeWhence(char[12], int); const char *DescribeWhichPrio(char[12], int); #define DescribeArchPrctlCode(x) DescribeArchPrctlCode(alloca(12), x) +#define DescribeCancelState(x, y) DescribeCancelState(alloca(12), x, y) #define DescribeCapability(x) DescribeCapability(alloca(32), x) #define DescribeClockName(x) DescribeClockName(alloca(32), x) #define DescribeDirfd(x) DescribeDirfd(alloca(12), x) @@ -116,6 +119,7 @@ const char *DescribeWhichPrio(char[12], int); #define DescribeSocketType(x) DescribeSocketType(alloca(64), x) #define DescribeStdioState(x) DescribeStdioState(alloca(12), x) #define DescribeStringList(x) DescribeStringList(alloca(300), x) +#define DescribeThreadCreateFlags(x) DescribeThreadCreateFlags(alloca(64), x) #define DescribeWhence(x) DescribeWhence(alloca(12), x) #define DescribeWhichPrio(x) DescribeWhichPrio(alloca(12), x) diff --git a/test/libc/calls/pwrite_test.c b/libc/intrin/describethreadcreationflags.c similarity index 77% rename from test/libc/calls/pwrite_test.c rename to libc/intrin/describethreadcreationflags.c index c22fbf266..af7c9d5dc 100644 --- a/test/libc/calls/pwrite_test.c +++ b/libc/intrin/describethreadcreationflags.c @@ -16,23 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/struct/stat.h" -#include "libc/sysv/consts/o.h" -#include "libc/testlib/testlib.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/processcreationflags.h" -char buf[8]; -struct stat st; -char testlib_enable_tmp_setup_teardown; +static const struct DescribeFlags kThreadCreationFlags[] = { + {kNtCreateSuspended, "kNtCreateSuspended"}, // + {kNtStackSizeParamIsAReservation, "kNtStackSizeParamIsAReservation"}, // +}; -void SetUpOnce(void) { - ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); -} - -TEST(pwrite, testWritePastEof_extendsFile) { - EXPECT_SYS(0, 3, creat("foo", 0644)); - EXPECT_SYS(0, 8, pwrite(3, buf, 8, 100)); - EXPECT_SYS(0, 0, fstat(3, &st)); - EXPECT_EQ(108, st.st_size); - EXPECT_SYS(0, 0, close(3)); +const char *(DescribeThreadCreateFlags)(char buf[64], uint32_t x) { + return DescribeFlags(buf, 64, kThreadCreationFlags, + ARRAYLEN(kThreadCreationFlags), "", x); } diff --git a/libc/intrin/dll.h b/libc/intrin/dll.h index a3ed82c5d..5f269f018 100644 --- a/libc/intrin/dll.h +++ b/libc/intrin/dll.h @@ -20,6 +20,10 @@ static inline void dll_init(struct Dll *e) { e->prev = e; } +static inline int dll_is_alone(struct Dll *e) { + return e->next == e && e->prev == e; +} + static inline int dll_is_empty(struct Dll *list) { return !list; } diff --git a/libc/intrin/tlsisrequired.c b/libc/intrin/enable_threads.c similarity index 94% rename from libc/intrin/tlsisrequired.c rename to libc/intrin/enable_threads.c index ddb7ca95b..bf75c1b16 100644 --- a/libc/intrin/tlsisrequired.c +++ b/libc/intrin/enable_threads.c @@ -16,11 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/strace.internal.h" +#include "libc/runtime/runtime.h" #include "libc/thread/tls.h" -void __require_tls(void) { - if (!__tls_enabled) { - notpossible; - } +void __enable_threads(void) { + __threaded = 1; } diff --git a/libc/intrin/exit.c b/libc/intrin/exit.c index df241b7aa..bc5a3c79f 100644 --- a/libc/intrin/exit.c +++ b/libc/intrin/exit.c @@ -23,9 +23,11 @@ #include "libc/nexgen32e/vendor.internal.h" #include "libc/nt/enum/status.h" #include "libc/nt/runtime.h" +#include "libc/nt/thunk/msabi.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/nr.h" +#include "libc/sysv/consts/sig.h" /** * Terminates process, ignoring destructors and atexit() handlers. @@ -101,7 +103,7 @@ wontreturn void _Exit(int exitcode) { if (waitstatus == kNtStillActive) { waitstatus = 0xc9af3d51u; } - ExitProcess(waitstatus); + TerminateThisProcess(waitstatus); } #ifdef __x86_64__ asm("push\t$0\n\t" diff --git a/libc/intrin/exit1.greg.c b/libc/intrin/exit1.greg.c index e7db87c6b..8c5d300c0 100644 --- a/libc/intrin/exit1.greg.c +++ b/libc/intrin/exit1.greg.c @@ -83,7 +83,7 @@ privileged wontreturn void _Exit1(int rc) { : "i"(93), "r"(r0) : "x8", "memory"); } else if (IsXnu()) { - __syslib->pthread_exit(0); + __syslib->__pthread_exit(0); } notpossible; #else diff --git a/libc/intrin/fds_lock.c b/libc/intrin/fds_lock.c index dedbabc99..2fdec4b75 100644 --- a/libc/intrin/fds_lock.c +++ b/libc/intrin/fds_lock.c @@ -17,18 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/state.internal.h" -#include "libc/str/str.h" #include "libc/thread/thread.h" -void(__fds_lock)(void) { +void __fds_lock(void) { pthread_mutex_lock(&__fds_lock_obj); } -void(__fds_unlock)(void) { +void __fds_unlock(void) { pthread_mutex_unlock(&__fds_lock_obj); } - -void __fds_funlock(void) { - bzero(&__fds_lock_obj, sizeof(__fds_lock_obj)); - __fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE; -} diff --git a/libc/fmt/formathex64.c b/libc/intrin/formathex64.c similarity index 100% rename from libc/fmt/formathex64.c rename to libc/intrin/formathex64.c diff --git a/libc/intrin/getmainstack.c b/libc/intrin/getmainstack.c new file mode 100644 index 000000000..0c955bfb7 --- /dev/null +++ b/libc/intrin/getmainstack.c @@ -0,0 +1,120 @@ +/*-*- 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/assert.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/rlimit.internal.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/rlim.h" +#include "libc/sysv/consts/rlimit.h" + +// Every UNIX system in our support vector creates arg blocks like: +// +// +// last environ string +// ... +// first environ string +// ... +// auxiliary value pointers +// environ pointers +// argument pointers +// argument count +// --- %rsp _start() +// ... +// ... +// ... program's stack +// ... +// ... +// +// +// The region of memory between highest and lowest can be computed +// across all supported platforms ±1 page accuracy as the distance +// between the last character of the last environ variable rounded +// up to the microprocessor page size (this computes the top addr) +// and the bottom is computed by subtracting RLIMIT_STACK rlim_cur +// It's simple but gets tricky if we consider environ can be empty + +static char *__get_last(char **list) { + char *res = 0; + for (int i = 0; list[i]; ++i) { + res = list[i]; + } + return res; +} + +static int __get_length(const char *s) { + int n = 0; + while (*s++) ++n; + return n; +} + +static uintptr_t __get_main_top(int pagesz) { + uintptr_t top; + const char *s; + if ((s = __get_last(__envp)) || (s = __get_last(__argv))) { + top = (uintptr_t)s + __get_length(s); + } else { + unsigned long *xp = __auxv; + while (*xp) xp += 2; + top = (uintptr_t)xp; + } + return ROUNDUP(top, pagesz); +} + +static size_t __get_stack_size(int pagesz, uintptr_t start, uintptr_t top) { + size_t size, max = 8 * 1024 * 1024; + struct rlimit rlim = {RLIM_INFINITY}; + sys_getrlimit(RLIMIT_STACK, &rlim); + if ((size = rlim.rlim_cur) > max) size = max; + return MAX(ROUNDUP(size, pagesz), ROUNDUP(top - start, pagesz)); +} + +/** + * Returns approximate boundaries of main thread stack. + */ +void __get_main_stack(void **out_addr, size_t *out_size, int *out_guardsize) { + if (IsWindows()) { + *out_addr = (void *)GetStaticStackAddr(0); + *out_size = GetStaticStackSize(); + *out_guardsize = 4096; + return; + } + int pagesz = getauxval(AT_PAGESZ); + uintptr_t start = (uintptr_t)__argv; + uintptr_t top = __get_main_top(pagesz); + uintptr_t bot = top - __get_stack_size(pagesz, start, top); + uintptr_t vdso = getauxval(AT_SYSINFO_EHDR); + if (vdso) { + if (vdso > start && vdso < top) { + top = vdso; + } else if (vdso < start && vdso >= bot) { + bot += vdso + pagesz * 2; + } + } + unassert(bot < top); + unassert(bot < start); + unassert(top > start); + *out_addr = (void *)bot; + *out_size = top - bot; + *out_guardsize = pagesz; +} diff --git a/libc/intrin/getminsigstksz.c b/libc/intrin/getminsigstksz.c new file mode 100644 index 000000000..a05195b45 --- /dev/null +++ b/libc/intrin/getminsigstksz.c @@ -0,0 +1,33 @@ +/*-*- 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/intrin/getauxval.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/ss.h" + +long __get_minsigstksz(void) { + struct AuxiliaryValue x; + x = __getauxval(AT_MINSIGSTKSZ); + if (x.isfound) { + return MAX(_MINSIGSTKSZ, x.value); + } else { + return _MINSIGSTKSZ; + } +} diff --git a/libc/intrin/pthread_getspecific.c b/libc/intrin/getsafesize.greg.c similarity index 63% rename from libc/intrin/pthread_getspecific.c rename to libc/intrin/getsafesize.greg.c index abcc81eb9..290677109 100644 --- a/libc/intrin/pthread_getspecific.c +++ b/libc/intrin/getsafesize.greg.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,26 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/intrin/atomic.h" +#include "libc/runtime/stack.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" #include "libc/thread/tls.h" +#include "libc/thread/tls2.internal.h" /** - * Gets value of TLS slot for current thread. + * Computes safer buffer size for alloca(). * - * If `k` wasn't created by pthread_key_create() then the behavior is - * undefined. If `k` was unregistered earlier by pthread_key_delete() - * then the behavior is undefined. + * @param want defines an upper limit on the return value + * @param extraspace is how much stack caller needs that isn't buffer + * @return number of bytes to use for your buffer, or negative if the + * allocation would likely cause a stack overflow */ -void *pthread_getspecific(pthread_key_t k) { - // "The effect of calling pthread_getspecific() or - // pthread_setspecific() with a key value not obtained from - // pthread_key_create() or after key has been deleted with - // pthread_key_delete() is undefined." - // ──Quoth POSIX.1-2017 - unassert(0 <= k && k < PTHREAD_KEYS_MAX); - unassert(atomic_load_explicit(_pthread_key_dtor + k, memory_order_acquire)); - return __get_tls()->tib_keys[k]; +privileged long __get_safe_size(long want, long extraspace) { + if (!__tls_enabled) return want; + struct PosixThread *pt; + struct CosmoTib *tib = __get_tls_privileged(); + long bottom, sp = GetStackPointer(); + if ((char *)sp >= tib->tib_sigstack_addr && + (char *)sp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) { + bottom = (long)tib->tib_sigstack_addr; + } else if ((pt = (struct PosixThread *)tib->tib_pthread) && + pt->attr.__stacksize) { + bottom = (long)pt->attr.__stackaddr + pt->attr.__guardsize; + } else { + return want; + } + long size = sp - bottom - extraspace; + if (size > want) size = want; + return size; } diff --git a/libc/intrin/gettid.c b/libc/intrin/gettid.c index 9f90c2c1f..f8be087ce 100644 --- a/libc/intrin/gettid.c +++ b/libc/intrin/gettid.c @@ -19,8 +19,10 @@ #include "libc/calls/calls.h" #include "libc/calls/state.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/likely.h" +#include "libc/sysv/errfuns.h" #include "libc/thread/tls.h" /** @@ -43,5 +45,9 @@ int gettid(void) { return tid; } } - return sys_gettid(); + if (IsXnuSilicon()) { + return enosys(); + } else { + return sys_gettid(); + } } diff --git a/libc/intrin/handlock.c b/libc/intrin/handlock.c index 742ea4ad6..95322662f 100644 --- a/libc/intrin/handlock.c +++ b/libc/intrin/handlock.c @@ -28,7 +28,7 @@ static nsync_mu __hand_mu; -void __hand_init(void) { +void __hand_wipe(void) { if (!SupportsWindows()) return; bzero(&__hand_mu, sizeof(__hand_mu)); } diff --git a/libc/intrin/handlock.internal.h b/libc/intrin/handlock.internal.h index b66405d2d..0e24ca546 100644 --- a/libc/intrin/handlock.internal.h +++ b/libc/intrin/handlock.internal.h @@ -3,7 +3,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void __hand_init(void); +void __hand_wipe(void); void __hand_rlock(void); void __hand_runlock(void); void __hand_lock(void); diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index d2181541b..9c18b93ed 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -49,17 +49,25 @@ $(LIBC_INTRIN_A).pkg: \ o/$(MODE)/libc/intrin/mman.greg.o: private COPTS += -Os $(LIBC_INTRIN_A_OBJS): private \ - CFLAGS += \ + COPTS += \ -x-no-pg \ -ffreestanding \ -fno-sanitize=all \ - -fno-stack-protector + -fno-stack-protector \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + +o/$(MODE)/libc/intrin/kprintf.o: private \ + CFLAGS += \ + -Wframe-larger-than=128 \ + -Walloca-larger-than=128 o/$(MODE)/libc/intrin/asan.o: private \ CFLAGS += \ -O2 \ -finline \ - -finline-functions + -finline-functions \ + -fpatchable-function-entry=0,0 o//libc/intrin/memmove.o: private \ CFLAGS += \ @@ -113,6 +121,8 @@ o/$(MODE)/libc/intrin/ktcpoptnames.o: libc/intrin/ktcpoptnames.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/intrin/sched_yield.o: libc/intrin/sched_yield.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< +o/$(MODE)/libc/intrin/stackcall.o: libc/intrin/stackcall.S + @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x))) LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/intrin/isdebuggerpresent.c b/libc/intrin/isdebuggerpresent.c index 96ecf5ec0..9ae20ed39 100644 --- a/libc/intrin/isdebuggerpresent.c +++ b/libc/intrin/isdebuggerpresent.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/cp.internal.h" +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -52,7 +52,7 @@ int IsDebuggerPresent(bool force) { if (!PLEDGED(RPATH)) return false; res = 0; e = errno; - BEGIN_CANCELLATION_POINT; + BLOCK_CANCELLATIONS; if ((fd = __sys_openat(AT_FDCWD, "/proc/self/status", O_RDONLY, 0)) >= 0) { if ((got = sys_read(fd, buf, sizeof(buf) - 1)) > 0) { buf[got] = '\0'; @@ -63,7 +63,7 @@ int IsDebuggerPresent(bool force) { } sys_close(fd); } - END_CANCELLATION_POINT; + ALLOW_CANCELLATIONS; errno = e; return res; } diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 54bb21e5d..fd434015d 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -43,6 +43,7 @@ #include "libc/limits.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" +#include "libc/mem/alloca.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/nt/createfile.h" @@ -73,10 +74,13 @@ #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/tls.h" #include "libc/thread/tls2.internal.h" #include "libc/vga/vga.internal.h" +#define STACK_ERROR "kprintf error: stack is about to overflow\n" + #define KGETINT(x, va, t, s) \ switch (t) { \ case -3: \ @@ -123,7 +127,6 @@ // clang-format off __msabi extern typeof(CreateFile) *const __imp_CreateFileW; __msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle; -__msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess; __msabi extern typeof(GetEnvironmentVariable) *const __imp_GetEnvironmentVariableW; __msabi extern typeof(GetLastError) *const __imp_GetLastError; __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; @@ -322,10 +325,8 @@ privileged long kloghandle(void) { e = __imp_GetLastError(); n = __imp_GetEnvironmentVariableW(u"KPRINTF_LOG", path, 512); if (!n && __imp_GetLastError() == kNtErrorEnvvarNotFound) { - long proc; - proc = __imp_GetCurrentProcess(); hand = __imp_GetStdHandle(kNtStdErrorHandle); - __imp_DuplicateHandle(proc, hand, proc, &hand, 0, false, + __imp_DuplicateHandle(-1, hand, -1, &hand, 0, false, kNtDuplicateSameAccess); } else if (n && n < 512) { hand = __imp_CreateFileW( @@ -423,6 +424,8 @@ privileged void klog(const char *b, size_t n) { : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } #elif defined(__aarch64__) + // this isn't a cancellation point because we don't acknowledge eintr + // on xnu we use the "nocancel" version of the system call for safety register long r0 asm("x0") = kloghandle(); register long r1 asm("x1") = (long)b; register long r2 asm("x2") = (long)n; @@ -740,11 +743,8 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, case 'G': x = va_arg(va, int); - if (_weaken(strsignal_r) && (s = _weaken(strsignal_r)(x, z))) { - goto FormatString; - } else { - goto FormatDecimal; - } + s = strsignal_r(x, z); + goto FormatString; case 't': { // %t will print the &symbol associated with an address. this @@ -1032,10 +1032,18 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) { * @vforksafe */ privileged void kvprintf(const char *fmt, va_list v) { - size_t n; - char b[4000]; - n = kformat(b, sizeof(b), fmt, v); - klog(b, MIN(n, sizeof(b) - 1)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + long size = __get_safe_size(8000, 3000); + if (size < 80) { + klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); + return; + } + char *buf = alloca(size); + CheckLargeStackAllocation(buf, size); +#pragma GCC pop_options + size_t count = kformat(buf, size, fmt, v); + klog(buf, MIN(count, size - 1)); } /** diff --git a/libc/intrin/memtrack.greg.c b/libc/intrin/memtrack.greg.c index ce6f17e4e..dba35bdfe 100644 --- a/libc/intrin/memtrack.greg.c +++ b/libc/intrin/memtrack.greg.c @@ -67,7 +67,6 @@ static bool __extend_memory(struct MemoryIntervals *mm) { base = (char *)kMemtrackStart; prot = PROT_READ | PROT_WRITE; flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED; - // TODO(jart): These map handles should not leak across NT fork() if (mm->p == mm->s) { // TODO(jart): How can we detect ASAN mode under GREG? if (1 || IsAsan()) { diff --git a/libc/intrin/mman.greg.c b/libc/intrin/mman.greg.c index f6f9ae3b6..abbf6fe00 100644 --- a/libc/intrin/mman.greg.c +++ b/libc/intrin/mman.greg.c @@ -47,6 +47,13 @@ #define INVERT(x) (BANE + PHYSICAL(x)) #define NOPAGE ((uint64_t)-1) +#define ABS64(x) \ + ({ \ + int64_t vAddr; \ + __asm__("movabs\t%1,%0" : "=r"(vAddr) : "i"(x)); \ + vAddr; \ + }) + struct ReclaimedPage { uint64_t next; }; @@ -252,37 +259,56 @@ textreal void __setup_mman(struct mman *mm, uint64_t *pml4t, uint64_t top) { __invert_memory(mm, pml4t); } +static textreal uint64_t __map_phdr(struct mman *mm, uint64_t *pml4t, + uint64_t b, uint64_t m, + struct Elf64_Phdr *p) { + uint64_t i, f, v; + f = PAGE_RSRV | PAGE_U; + if (p->p_flags & PF_W) + f |= PAGE_V | PAGE_RW; + else if (p->p_flags & (PF_R | PF_X)) + f |= PAGE_V; + if (!(p->p_flags & PF_X)) f |= PAGE_XD; + for (i = 0; i < p->p_memsz; i += 4096) { + if (i < p->p_filesz) { + v = b + p->p_offset + i; + m = MAX(m, v); + } else { + v = __clear_page(BANE + __new_page(mm)); + } + *__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f; + __ref_page(mm, pml4t, v & PAGE_TA); + } + return m; +} + /** * Maps APE-defined ELF program headers into memory and clears BSS. */ textreal void __map_phdrs(struct mman *mm, uint64_t *pml4t, uint64_t b, uint64_t top) { - uint64_t i, f, v, m; + uint64_t m; struct Elf64_Phdr *p; extern char ape_phdrs[] __attribute__((__weak__)); extern char ape_phdrs_end[] __attribute__((__weak__)); + extern char ape_stack_pf[] __attribute__((__weak__)); + extern char ape_stack_offset[] __attribute__((__weak__)); + extern char ape_stack_vaddr[] __attribute__((__weak__)); + extern char ape_stack_filesz[] __attribute__((__weak__)); + extern char ape_stack_memsz[] __attribute__((__weak__)); __setup_mman(mm, pml4t, top); for (p = (struct Elf64_Phdr *)INVERT(ape_phdrs), m = 0; p < (struct Elf64_Phdr *)INVERT(ape_phdrs_end); ++p) { - if (p->p_type == PT_LOAD || p->p_type == PT_GNU_STACK) { - f = PAGE_RSRV | PAGE_U; - if (p->p_flags & PF_W) - f |= PAGE_V | PAGE_RW; - else if (p->p_flags & (PF_R | PF_X)) - f |= PAGE_V; - if (!(p->p_flags & PF_X)) f |= PAGE_XD; - for (i = 0; i < p->p_memsz; i += 4096) { - if (i < p->p_filesz) { - v = b + p->p_offset + i; - m = MAX(m, v); - } else { - v = __clear_page(BANE + __new_page(mm)); - } - *__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f; - __ref_page(mm, pml4t, v & PAGE_TA); - } - } + m = __map_phdr(mm, pml4t, b, m, p); } + m = __map_phdr(mm, pml4t, b, m, + &(struct Elf64_Phdr){ + .p_flags = (uintptr_t)ape_stack_pf, + .p_offset = (uintptr_t)ape_stack_offset, + .p_vaddr = ABS64(ape_stack_vaddr), + .p_filesz = (uintptr_t)ape_stack_filesz, + .p_memsz = (uintptr_t)ape_stack_memsz, + }); mm->pdp = MAX(mm->pdp, m); } diff --git a/libc/intrin/mmi_lock.c b/libc/intrin/mmi_lock.c index c77359bd8..87fb1e272 100644 --- a/libc/intrin/mmi_lock.c +++ b/libc/intrin/mmi_lock.c @@ -17,17 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/memtrack.internal.h" -#include "libc/str/str.h" #include "libc/thread/thread.h" -// this lock currently needs to be (1) recursive and (2) not nsync - extern pthread_mutex_t __mmi_lock_obj; -void(__mmi_lock)(void) { +void __mmi_lock(void) { pthread_mutex_lock(&__mmi_lock_obj); } -void(__mmi_unlock)(void) { +void __mmi_unlock(void) { pthread_mutex_unlock(&__mmi_lock_obj); } diff --git a/libc/calls/ntcontext2linux.c b/libc/intrin/ntcontext2linux.c similarity index 100% rename from libc/calls/ntcontext2linux.c rename to libc/intrin/ntcontext2linux.c diff --git a/libc/intrin/pthread_atfork.c b/libc/intrin/pthread_atfork.c index 08c3213c8..f0e94401d 100644 --- a/libc/intrin/pthread_atfork.c +++ b/libc/intrin/pthread_atfork.c @@ -34,6 +34,10 @@ * // data structures... * } g_lib; * + * static void lib_wipe(void) { + * pthread_mutex_init(&g_lib.lock, 0); + * } + * * static void lib_lock(void) { * pthread_mutex_lock(&g_lib.lock); * } @@ -42,13 +46,9 @@ * pthread_mutex_unlock(&g_lib.lock); * } * - * static void lib_funlock(void) { - * pthread_mutex_init(&g_lib.lock, 0); - * } - * * static void lib_setup(void) { - * lib_funlock(); - * pthread_atfork(lib_lock, lib_unlock, lib_funlock); + * lib_wipe(); + * pthread_atfork(lib_lock, lib_unlock, lib_wipe); * } * * static void lib_init(void) { @@ -62,14 +62,6 @@ * lib_unlock(); * } * - * This won't actually aspect fork() until pthread_create() is called, - * since we don't want normal non-threaded programs to have to acquire - * exclusive locks on every resource in the entire app just to fork(). - * - * The vfork() function is *never* aspected. What happens instead is a - * global variable named `__vforked` is set to true in the child which - * causes lock functions to do nothing. So far, it works like a charm. - * * @param prepare is run by fork() before forking happens * @param parent is run by fork() after forking happens in parent process * @param child is run by fork() after forking happens in childe process diff --git a/libc/intrin/pthread_cleanup_pop.c b/libc/intrin/pthread_cleanup_pop.c index abe8de4a8..f49754fff 100644 --- a/libc/intrin/pthread_cleanup_pop.c +++ b/libc/intrin/pthread_cleanup_pop.c @@ -23,7 +23,7 @@ void(pthread_cleanup_pop)(struct _pthread_cleanup_buffer *cb, int execute) { struct PosixThread *pt; - if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + if (__tls_enabled && (pt = _pthread_self())) { unassert(cb == pt->cleanup); pt->cleanup = cb->__prev; } diff --git a/libc/intrin/pthread_cleanup_push.c b/libc/intrin/pthread_cleanup_push.c index b294c6d21..8d0123ce8 100644 --- a/libc/intrin/pthread_cleanup_push.c +++ b/libc/intrin/pthread_cleanup_push.c @@ -25,7 +25,7 @@ void(pthread_cleanup_push)(struct _pthread_cleanup_buffer *cb, struct PosixThread *pt; cb->__routine = routine; cb->__arg = arg; - if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + if (__tls_enabled && (pt = _pthread_self())) { cb->__prev = pt->cleanup; pt->cleanup = cb; } diff --git a/libc/calls/wincrashearly.c b/libc/intrin/pthread_main.c similarity index 92% rename from libc/calls/wincrashearly.c rename to libc/intrin/pthread_main.c index f2da22651..068aa25f1 100644 --- a/libc/calls/wincrashearly.c +++ b/libc/intrin/pthread_main.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,5 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/thread/posixthread.internal.h" -int64_t __wincrashearly; +struct PosixThread _pthread_static; diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 955488083..0c7047f74 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -67,14 +67,13 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) { int t; - if ((!__threaded && mutex->_pshared != PTHREAD_PROCESS_SHARED) || __vforked) { + LOCKTRACE("pthread_mutex_lock(%t)", mutex); + + if (__vforked) { return 0; } - LOCKTRACE("pthread_mutex_lock(%t)", mutex); - - if (__tls_enabled && // - mutex->_type == PTHREAD_MUTEX_NORMAL && // + if (mutex->_type == PTHREAD_MUTEX_NORMAL && // mutex->_pshared == PTHREAD_PROCESS_PRIVATE && // _weaken(nsync_mu_lock)) { _weaken(nsync_mu_lock)((nsync_mu *)mutex); diff --git a/libc/intrin/pthread_mutex_unlock.c b/libc/intrin/pthread_mutex_unlock.c index 158688c7c..c5b3bac37 100644 --- a/libc/intrin/pthread_mutex_unlock.c +++ b/libc/intrin/pthread_mutex_unlock.c @@ -39,14 +39,9 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) { int t; - if ((!__threaded && mutex->_pshared != PTHREAD_PROCESS_SHARED) || __vforked) { - return 0; - } - LOCKTRACE("pthread_mutex_unlock(%t)", mutex); - if (__tls_enabled && // - mutex->_type == PTHREAD_MUTEX_NORMAL && // + if (mutex->_type == PTHREAD_MUTEX_NORMAL && // mutex->_pshared == PTHREAD_PROCESS_PRIVATE && // _weaken(nsync_mu_unlock)) { _weaken(nsync_mu_unlock)((nsync_mu *)mutex); diff --git a/libc/intrin/pthread_next.c b/libc/intrin/pthread_next.c new file mode 100644 index 000000000..ccba40b1c --- /dev/null +++ b/libc/intrin/pthread_next.c @@ -0,0 +1,32 @@ +/*-*- 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/assert.h" +#include "libc/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/thread/posixthread.internal.h" + +intptr_t _pthread_syshand(struct PosixThread *pt) { + intptr_t syshand; + unassert(IsWindows() || IsXnuSilicon()); + for (;;) { + syshand = atomic_load_explicit(&pt->tib->tib_syshand, memory_order_acquire); + if (syshand) return syshand; + pthread_yield(); + } +} diff --git a/libc/intrin/pthread_setcancelstate.c b/libc/intrin/pthread_setcancelstate.c index 2de212601..0763583da 100644 --- a/libc/intrin/pthread_setcancelstate.c +++ b/libc/intrin/pthread_setcancelstate.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/blockcancel.internal.h" +#include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" @@ -45,37 +47,46 @@ * @asyncsignalsafe */ errno_t pthread_setcancelstate(int state, int *oldstate) { + errno_t err; struct PosixThread *pt; - if (__tls_enabled && (pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + if (__tls_enabled && (pt = _pthread_self())) { switch (state) { case PTHREAD_CANCEL_ENABLE: case PTHREAD_CANCEL_DISABLE: case PTHREAD_CANCEL_MASKED: if (oldstate) { - if (pt->flags & PT_NOCANCEL) { + if (pt->pt_flags & PT_NOCANCEL) { *oldstate = PTHREAD_CANCEL_DISABLE; - } else if (pt->flags & PT_MASKED) { + } else if (pt->pt_flags & PT_MASKED) { *oldstate = PTHREAD_CANCEL_MASKED; } else { *oldstate = PTHREAD_CANCEL_ENABLE; } } - pt->flags &= ~(PT_NOCANCEL | PT_MASKED); + pt->pt_flags &= ~(PT_NOCANCEL | PT_MASKED); if (state == PTHREAD_CANCEL_MASKED) { - pt->flags |= PT_MASKED; + pt->pt_flags |= PT_MASKED; } else if (state == PTHREAD_CANCEL_DISABLE) { - pt->flags |= PT_NOCANCEL; + pt->pt_flags |= PT_NOCANCEL; } - return 0; + err = 0; + break; default: - return EINVAL; + err = EINVAL; + break; } } else { if (oldstate) { *oldstate = 0; } - return 0; + err = 0; } +#if IsModeDbg() + STRACE("pthread_setcancelstate(%s, [%s]) → %s", + DescribeCancelState(0, &state), DescribeCancelState(err, oldstate), + DescribeErrno(err)); +#endif + return err; } int _pthread_block_cancellations(void) { diff --git a/libc/intrin/sigisprecious.c b/libc/intrin/pthread_tid.c similarity index 84% rename from libc/intrin/sigisprecious.c rename to libc/intrin/pthread_tid.c index 1d3bf80c1..cf11a2810 100644 --- a/libc/intrin/sigisprecious.c +++ b/libc/intrin/pthread_tid.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,15 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sigset.h" -#include "libc/sysv/consts/sig.h" +#include "libc/intrin/atomic.h" +#include "libc/thread/posixthread.internal.h" -/** - * Returns true if you're not authorized to block this signal. - */ -int sigisprecious(int sig) { - return 0 -#define M(x) || sig == x -#include "libc/intrin/sigisprecious.inc" - ; +int _pthread_tid(struct PosixThread *pt) { + int tid = 0; + while (pt && !(tid = atomic_load_explicit(&pt->ptid, memory_order_acquire))) { + pthread_yield(); + } + return tid; } diff --git a/libc/intrin/pthread_yield.c b/libc/intrin/pthread_yield.c index 46b3595e5..8090c43ce 100644 --- a/libc/intrin/pthread_yield.c +++ b/libc/intrin/pthread_yield.c @@ -17,7 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/intrin/strace.internal.h" +#include "libc/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/syslib.internal.h" #include "libc/thread/thread.h" /** @@ -26,7 +28,10 @@ * @return 0 on success, or error number on failure */ int pthread_yield(void) { - sched_yield(); - STRACE("pthread_yield()"); + if (IsXnuSilicon()) { + __syslib->__pthread_yield_np(); + } else { + sched_yield(); + } return 0; } diff --git a/libc/intrin/putenv.c b/libc/intrin/putenv.c index 7aa732410..fb9655676 100644 --- a/libc/intrin/putenv.c +++ b/libc/intrin/putenv.c @@ -48,6 +48,8 @@ static char **GrowEnviron(char **a) { for (p = b; *a;) { *p++ = *a++; } + } else { + b[0] = 0; } environ = b; expected = b; diff --git a/libc/intrin/setenv.c b/libc/intrin/setenv.c index 5af9c4a95..19e91c195 100644 --- a/libc/intrin/setenv.c +++ b/libc/intrin/setenv.c @@ -29,8 +29,8 @@ * Copies variable to environment. * * @return 0 on success, or -1 w/ errno and environment is unchanged + * @raise ENOMEM if out of memory or malloc() wasn't linked * @raise EINVAL if `name` is empty or contains `'='` - * @raise ENOMEM if we require more vespene gas * @see putenv(), getenv() * @threadunsafe */ diff --git a/libc/calls/g_sighandrvas.c b/libc/intrin/sighandrvas.c similarity index 100% rename from libc/calls/g_sighandrvas.c rename to libc/intrin/sighandrvas.c diff --git a/libc/calls/switchstacks.S b/libc/intrin/stackcall.S similarity index 81% rename from libc/calls/switchstacks.S rename to libc/intrin/stackcall.S index 681f48415..b03012384 100644 --- a/libc/calls/switchstacks.S +++ b/libc/intrin/stackcall.S @@ -18,17 +18,41 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -__switch_stacks: +// Calls function on different stack. +// +// @param %rdi is arg1 +// @param %rsi is arg2 +// @param %rdx is arg3 +// @param %rcx is arg4 +// @param %r8 is func +// @param %r9 is stack +// @return %rax is res +__stack_call: #ifdef __x86_64__ + push %rbx + push %r15 + mov %rbp,%r15 + mov %rsp,%rbx mov %r9,%rsp - and $-16,%rsp xor %rbp,%rbp call *%r8 + mov %r15,%rbp + mov %rbx,%rsp + pop %r15 + pop %rbx + ret #elif defined(__aarch64__) + stp x29,x30,[sp,#-32]! + str x27,[sp,16] + mov x27,sp and sp,x5,#-16 mov x29,0 blr x4 + mov sp,x27 + ldr x27,[sp,16] + ldp x29,x30,[sp],#32 + ret #else #error "unsupported architecture" #endif - .endfn __switch_stacks,globl + .endfn __stack_call,globl diff --git a/libc/intrin/strace.internal.h b/libc/intrin/strace.internal.h index 141b50308..e3c27ce65 100644 --- a/libc/intrin/strace.internal.h +++ b/libc/intrin/strace.internal.h @@ -53,7 +53,7 @@ COSMOPOLITAN_C_START_ #endif #if defined(SYSDEBUG) && _NTTRACE -#define NTTRACE(FMT, ...) STRACE(FMT, ##__VA_ARGS__) +#define NTTRACE(FMT, ...) STRACE("\e[2m" FMT "\e[0m", ##__VA_ARGS__) #else #define NTTRACE(FMT, ...) (void)0 #endif diff --git a/libc/intrin/sys_gettid.greg.c b/libc/intrin/sys_gettid.greg.c index 1f6730d50..73b765b3e 100644 --- a/libc/intrin/sys_gettid.greg.c +++ b/libc/intrin/sys_gettid.greg.c @@ -64,7 +64,6 @@ int sys_gettid(void) { return tid; #elif defined(__aarch64__) // this can't be used on xnu - if (!IsLinux()) notpossible; register long res asm("x0"); asm volatile("mov\tx8,%1\n\t" "svc\t0" diff --git a/libc/calls/tgkill.c b/libc/intrin/terminatethisprocess.c similarity index 79% rename from libc/calls/tgkill.c rename to libc/intrin/terminatethisprocess.c index 5dc8b808a..cdc9864f5 100644 --- a/libc/calls/tgkill.c +++ b/libc/intrin/terminatethisprocess.c @@ -16,21 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/syscall-sysv.internal.h" -#include "libc/intrin/strace.internal.h" +#include "libc/nt/runtime.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess; /** - * Kills thread, the Linux way. - * - * @param tgid is thread group id, which on Linux means process id - * @param tid is thread id - * @raises ENOSYS on non-Linux - * @see tkill() + * Terminates the calling process and all of its threads. */ -int tgkill(int tgid, int tid, int sig) { - int rc; - rc = sys_tgkill(tgid, tid, sig); - STRACE("tgkill(%d, %d, %G) → %d% m", tgid, tid, sig, rc); - return rc; +textwindows dontinstrument void TerminateThisProcess(uint32_t dwWaitStatus) { + // "When a process terminates itself, TerminateProcess stops execution + // of the calling thread and does not return." -Quoth MSDN + __imp_TerminateProcess(-1, dwWaitStatus); + __builtin_unreachable(); } diff --git a/libc/intrin/waitforsingleobject.c b/libc/intrin/waitforsingleobject.c index 375e70e72..96ccc9681 100644 --- a/libc/intrin/waitforsingleobject.c +++ b/libc/intrin/waitforsingleobject.c @@ -25,12 +25,11 @@ __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; /** * Waits for handle to change status. - * @note this wrapper takes care of ABI, STRACE(), and __winerr() + * @return -1u on error w/ GetLastError() */ uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds) { uint32_t rc; rc = __imp_WaitForSingleObject(hHandle, dwMilliseconds); - if (rc == -1u) __winerr(); POLLTRACE("WaitForSingleObject(%ld, %'d) → %d% m", hHandle, dwMilliseconds, rc); return rc; diff --git a/libc/intrin/bo.c b/libc/intrin/wintlsinit.c similarity index 71% rename from libc/intrin/bo.c rename to libc/intrin/wintlsinit.c index 806e88f78..c252b5466 100644 --- a/libc/intrin/bo.c +++ b/libc/intrin/wintlsinit.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,32 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/bo.internal.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/log/libfatal.internal.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/thread/tls.h" +#include "libc/thread/tls2.internal.h" -int begin_blocking_operation(void) { - int state = 0; - struct CosmoTib *tib; - struct PosixThread *pt; - if (__tls_enabled) { - tib = __get_tls(); - if ((pt = (struct PosixThread *)tib->tib_pthread)) { - state = pt->flags & PT_BLOCKED; - pt->flags |= PT_BLOCKED; - } - } - return state; -} +__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; -void end_blocking_operation(int state) { - struct CosmoTib *tib; - struct PosixThread *pt; - if (__tls_enabled) { - tib = __get_tls(); - if ((pt = (struct PosixThread *)tib->tib_pthread)) { - pt->flags &= ~PT_BLOCKED; - pt->flags |= state; - } - } +/** + * Initializes bare minimum TLS for Windows thread. + */ +textwindows dontinstrument void __bootstrap_tls(struct CosmoTib *tib, + char *bp) { + __repstosb(tib, 0, sizeof(*tib)); + tib->tib_self = tib; + tib->tib_self2 = tib; + tib->tib_sigmask = -1; + tib->tib_sigstack_size = 57344; + tib->tib_sigstack_addr = bp - 57344; + tib->tib_tid = __imp_GetCurrentThreadId(); + __set_tls_win32(tib); } diff --git a/libc/isystem/spawn.h b/libc/isystem/spawn.h index 638126051..ef2054d1f 100644 --- a/libc/isystem/spawn.h +++ b/libc/isystem/spawn.h @@ -1,5 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_ISYSTEM_SPAWN_H_ #define COSMOPOLITAN_LIBC_ISYSTEM_SPAWN_H_ #include "libc/calls/weirdtypes.h" -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #endif /* COSMOPOLITAN_LIBC_ISYSTEM_SPAWN_H_ */ diff --git a/libc/libc.mk b/libc/libc.mk index e0ad8f07d..a45865d8c 100644 --- a/libc/libc.mk +++ b/libc/libc.mk @@ -24,6 +24,7 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \ o/$(MODE)/libc/mem \ o/$(MODE)/libc/nexgen32e \ o/$(MODE)/libc/nt \ + o/$(MODE)/libc/proc \ o/$(MODE)/libc/runtime \ o/$(MODE)/libc/sock \ o/$(MODE)/libc/stdio \ diff --git a/libc/log/addr2linepath.c b/libc/log/addr2linepath.c index 86109138f..7a75c8329 100644 --- a/libc/log/addr2linepath.c +++ b/libc/log/addr2linepath.c @@ -16,35 +16,48 @@ │ 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/cosmo.h" +#include "libc/errno.h" #include "libc/limits.h" -#include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" -static const char *TryMonoRepoPath(const char *var, const char *path) { +#ifdef __x86_64__ +#define ADDR2LINE "o/third_party/gcc/bin/x86_64-linux-musl-addr2line" +#elif defined(__aarch64__) +#define ADDR2LINE "o/third_party/gcc/bin/aarch64-linux-musl-addr2line" +#endif + +static struct { + atomic_uint once; + char *res; char buf[PATH_MAX]; - if (getenv(var)) return 0; - if (!isexecutable(path)) return 0; - if (*path != '/') { - if (getcwd(buf, sizeof(buf)) <= 0) return 0; - strlcat(buf, "/", sizeof(buf)); - strlcat(buf, path, sizeof(buf)); - path = buf; +} g_addr2line; + +const void GetAddr2linePathInit(void) { + int e = errno; + const char *path; + if (!(path = getenv("ADDR2LINE"))) { + path = ADDR2LINE; } - setenv(var, path, false); - return getenv(var); + char *buf = g_addr2line.buf; + if (isexecutable(path)) { + if (*path != '/' && getcwd(buf, PATH_MAX)) { + strlcat(buf, "/", PATH_MAX); + } + strlcat(buf, path, PATH_MAX); + } + if (*buf) { + g_addr2line.res = buf; + } else { + g_addr2line.res = commandv("addr2line", buf, PATH_MAX); + } + errno = e; } const char *GetAddr2linePath(void) { - const char *s = 0; -#ifdef __x86_64__ - s = TryMonoRepoPath("ADDR2LINE", - "o/third_party/gcc/bin/x86_64-linux-musl-addr2line"); -#elif defined(__aarch64__) - s = TryMonoRepoPath("ADDR2LINE", - "o/third_party/gcc/bin/aarch64-linux-musl-addr2line"); -#endif - if (!s) s = commandvenv("ADDR2LINE", "addr2line"); - return s; + cosmo_once(&g_addr2line.once, GetAddr2linePathInit); + return g_addr2line.res; } diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c index e5b8e4678..84f39fd91 100644 --- a/libc/log/appendresourcereport.c +++ b/libc/log/appendresourcereport.c @@ -60,9 +60,9 @@ static void AppendUnit(struct State *s, int64_t x, const char *t) { * Generates process resource usage report. */ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { + double ticks; struct State s; long utime, stime; - long double ticks; struct State *st = &s; s.b = b; s.nl = nl; @@ -75,10 +75,10 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { appends(b, "needed "); AppendInt(st, utime + stime); appends(b, "us cpu ("); - AppendInt(st, (long double)stime / (utime + stime) * 100); + AppendInt(st, (double)stime / (utime + stime) * 100); appends(b, "% kernel)"); AppendNl(st); - ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); + ticks = ceill((double)(utime + stime) / (1000000.L / CLK_TCK)); if (ru->ru_idrss) { AppendMetric(st, "needed ", lroundl(ru->ru_idrss / ticks), "kb private on average"); @@ -96,8 +96,8 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { appends(b, "caused "); AppendInt(st, ru->ru_minflt + ru->ru_majflt); appends(b, " page faults ("); - AppendInt( - st, (long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * 100); + AppendInt(st, + (double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * 100); appends(b, "% memcpy)"); AppendNl(st); } @@ -108,8 +108,7 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { appendw(b, READ16LE("es")); } appendw(b, READ16LE(" (")); - AppendInt(st, - (long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); + AppendInt(st, (double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); appends(b, "% consensual)"); AppendNl(st); } @@ -129,7 +128,7 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { AppendNl(st); } if (ru->ru_nsignals) { - appends(b, "received "); + appends(b, "delivered "); AppendUnit(st, ru->ru_nsignals, "signal"); AppendNl(st); } diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index d9f8af4a3..bd461595e 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -67,7 +67,7 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols( } addr = frame->addr; #ifdef __x86_64__ - if (addr == (intptr_t)_weaken(__gc)) { + if (gi && addr == (intptr_t)_weaken(__gc)) { do { --gi; } while ((addr = garbage->p[gi].ret) == (intptr_t)_weaken(__gc)); diff --git a/libc/log/die.c b/libc/log/die.c index 4626f2ee2..e59e2072a 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -17,36 +17,40 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/syscall-sysv.internal.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/describebacktrace.internal.h" #include "libc/intrin/kprintf.h" -#include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" -#include "libc/log/log.h" -#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" -#if SupportsMetal() -__static_yoink("_idt"); -#endif - /** - * Aborts process after printing a backtrace. + * Exits process with crash report. + * + * The `cosmoaddr2line` command may be copied and pasted into the shell + * to obtain further details such as function calls and source lines in + * the backtrace. Unlike abort() this function doesn't depend on signal + * handling infrastructure. If tcsetattr() was called earlier to change + * terminal settings, then they'll be restored automatically. Your exit + * handlers won't be called. The `KPRINTF_LOG` environment variable may + * configure the output location of these reports, defaulting to stderr + * which is duplicated at startup, in case the program closes the file. + * + * @see __minicrash() for signal handlers, e.g. handling abort() + * @asyncsignalsafe + * @threadsafe + * @vforksafe */ -relegated dontasan wontreturn void __die(void) { - - // print vital error nubers reliably - // the surface are of code this calls is small and audited - kprintf("\r\n\e[1;31m__die %s pid %d tid %d bt %s\e[0m\n", - program_invocation_short_name, getpid(), sys_gettid(), - DescribeBacktrace(__builtin_frame_address(0))); - - // print much friendlier backtrace less reliably - // we're in a broken runtime state and so much can go wrong +relegated wontreturn void __die(void) { + char host[128]; __restore_tty(); - ShowBacktrace(2, __builtin_frame_address(0)); + strcpy(host, "unknown"); + gethostname(host, sizeof(host)); + kprintf("%serror: %s on %s pid %d tid %d has perished%s\n" + " cosmoaddr2line %s%s %s\n", + __nocolor ? "" : "\e[1;31m", program_invocation_short_name, host, + getpid(), gettid(), __nocolor ? "" : "\e[0m", __argv[0], + endswith(__argv[0], ".com") ? ".dbg" : "", + DescribeBacktrace(__builtin_frame_address(0))); _Exit(77); } diff --git a/libc/log/gdb.h b/libc/log/gdb.h index dff0ead35..2953aa07d 100644 --- a/libc/log/gdb.h +++ b/libc/log/gdb.h @@ -2,8 +2,8 @@ #define COSMOPOLITAN_LIBC_LOG_GDB_H_ #include "libc/calls/calls.h" #include "libc/calls/struct/rusage.h" -#include "libc/calls/wait4.h" #include "libc/dce.h" +#include "libc/proc/proc.internal.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/w.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/log/internal.h b/libc/log/internal.h index 9f69f3cde..60e1d1747 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -10,9 +10,7 @@ extern bool g_isrunningundermake; void __start_fatal(const char *, int); void __restore_tty(void); -void RestoreDefaultCrashSignalHandlers(void); -void __oncrash_amd64(int, struct siginfo *, void *) relegated; -void __oncrash_arm64(int, struct siginfo *, void *) relegated; +void __oncrash(int, struct siginfo *, void *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/leaks.c b/libc/log/leaks.c index 94466dabc..048f18410 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -26,7 +26,10 @@ #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" #include "libc/testlib/testlib.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" __static_yoink("GetSymbolByAddr"); @@ -82,10 +85,17 @@ static dontasan bool HasLeaks(void) { dontasan void CheckForMemoryLeaks(void) { struct mallinfo mi; if (!IsAsan()) return; // we need traces to exclude leaky + if (!GetSymbolTable()) { + kprintf("CheckForMemoryLeaks() needs the symbol table\n"); + return; + } if (!_cmpxchg(&once, false, true)) { kprintf("CheckForMemoryLeaks() may only be called once\n"); - exit(1); + exit(0); } + _pthread_unwind(_pthread_self()); + _pthread_unkey(__get_tls()); + _pthread_ungarbage(); __cxa_finalize(0); STRACE("checking for memory leaks% m"); if (!IsAsan()) { diff --git a/libc/log/log.mk b/libc/log/log.mk index 336ee67ff..5195f0c6e 100644 --- a/libc/log/log.mk +++ b/libc/log/log.mk @@ -32,11 +32,13 @@ LIBC_LOG_A_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ LIBC_NT_NTDLL \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ LIBC_SYSV_CALLS \ + LIBC_THREAD \ LIBC_TIME \ LIBC_TINYMATH \ THIRD_PARTY_COMPILER_RT \ @@ -54,10 +56,14 @@ $(LIBC_LOG_A).pkg: \ $(LIBC_LOG_A_OBJS) \ $(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/log/backtrace2.o \ -o/$(MODE)/libc/log/backtrace3.o: private \ - CFLAGS += \ - -fno-sanitize=all +# offer assurances about the stack safety of cosmo libc +$(LIBC_LOG_A_OBJS): private COPTS += -Wframe-larger-than=4096 -Walloca-larger-than=4096 + +$(LIBC_RUNTIME_A_OBJS): private \ + COPTS += \ + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 o/$(MODE)/libc/log/checkfail.o: private \ CFLAGS += \ @@ -67,22 +73,6 @@ o/$(MODE)/libc/log/watch.o: private \ CFLAGS += \ -ffreestanding -o/$(MODE)/libc/log/watch.o \ -o/$(MODE)/libc/log/attachdebugger.o \ -o/$(MODE)/libc/log/checkaligned.o \ -o/$(MODE)/libc/log/checkfail.o \ -o/$(MODE)/libc/log/checkfail_ndebug.o \ -o/$(MODE)/libc/log/restoretty.o \ -o/$(MODE)/libc/log/oncrash_amd64.o \ -o/$(MODE)/libc/log/oncrash_arm64.o \ -o/$(MODE)/libc/log/onkill.o \ -o/$(MODE)/libc/log/startfatal.o \ -o/$(MODE)/libc/log/startfatal_ndebug.o \ -o/$(MODE)/libc/log/ubsan.o \ -o/$(MODE)/libc/log/die.o: private \ - CFLAGS += \ - $(NO_MAGIC) - LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x))) LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS)) LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/log/minicrash.c b/libc/log/minicrash.c new file mode 100644 index 000000000..6638109f2 --- /dev/null +++ b/libc/log/minicrash.c @@ -0,0 +1,70 @@ +/*-*- 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/calls/struct/siginfo.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" +#include "libc/errno.h" +#include "libc/intrin/describebacktrace.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/internal.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" + +/** + * Prints miniature crash report. + * + * This function may be called from a signal handler to print vital + * information about the cause of a crash. Only vital number values + * shall be printed. The `cosmoaddr2line` command may be copied and + * pasted into the shell to obtain further details such as function + * calls and source lines in the backtrace. + * + * This function may be used as a sigaction handler, so long as the + * `SA_RESETHAND` flag is used. Using `SA_ONSTACK` is also a lovely + * feature since sigaltstack() is needed to report stack overflows. + * + * This implementation is designed to be: + * + * 1. Reliable under broken runtime states + * 2. Require only a few kB of stack + * 3. Have minimal binary footprint + * + * @see __die() for crashing from normal code without aborting + * @asyncsignalsafe + * @threadsafe + * @vforksafe + */ +relegated dontinstrument void __minicrash(int sig, siginfo_t *si, void *arg) { + char host[128]; + ucontext_t *ctx = arg; + strcpy(host, "unknown"); + gethostname(host, sizeof(host)); + kprintf( + "%serror: %s on %s pid %d tid %d got %G%s code %d addr %p%s\n" + "cosmoaddr2line %s%s %lx %s\n", + __nocolor ? "" : "\e[1;31m", program_invocation_short_name, host, + getpid(), gettid(), sig, + __is_stack_overflow(si, ctx) ? " (stack overflow)" : "", si->si_code, + si->si_addr, __nocolor ? "" : "\e[0m", __argv[0], + endswith(__argv[0], ".com") ? ".dbg" : "", ctx ? ctx->uc_mcontext.PC : 0, + DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.BP + : (struct StackFrame *)__builtin_frame_address(0))); +} diff --git a/libc/log/oncrash_amd64.c b/libc/log/oncrash_amd64.c index b80bbe9a1..2731fa1c9 100644 --- a/libc/log/oncrash_amd64.c +++ b/libc/log/oncrash_amd64.c @@ -18,10 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/atomic.h" +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/struct/utsname.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/ucontext.h" @@ -41,10 +44,12 @@ #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/math.h" +#include "libc/mem/alloca.h" #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/internal.h" #include "libc/runtime/pc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" @@ -56,6 +61,8 @@ __static_yoink("strerror_wr"); // for kprintf %m __static_yoink("strsignal_r"); // for kprintf %G +#define STACK_ERROR "error: not enough room on stack to print crash report\n" + static const char kGregOrder[17] forcealign(1) = { 13, 11, 8, 14, 12, 9, 10, 15, 16, 0, 1, 2, 3, 4, 5, 6, 7, }; @@ -69,16 +76,15 @@ static const char kCpuFlags[12] forcealign(1) = "CVPRAKZSTIDO"; static const char kFpuExceptions[6] forcealign(1) = "IDZOUP"; relegated static void ShowFunctionCalls(ucontext_t *ctx) { - struct StackFrame *bp; - struct StackFrame goodframe; - if (!ctx->uc_mcontext.rip) { - kprintf("%s is NULL can't show backtrace\n", "RIP"); - } else { - goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; - goodframe.addr = ctx->uc_mcontext.rip; - bp = &goodframe; - ShowBacktrace(2, bp); - } + kprintf( + "cosmoaddr2line %s%s %lx %s\n\n", __argv[0], + endswith(__argv[0], ".com") ? ".dbg" : "", ctx ? ctx->uc_mcontext.PC : 0, + DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.BP + : (struct StackFrame *)__builtin_frame_address(0))); + ShowBacktrace(2, &(struct StackFrame){ + .next = (struct StackFrame *)ctx->uc_mcontext.rbp, + .addr = ctx->uc_mcontext.rip, + }); } relegated static char *AddFlag(char *p, int b, const char *s) { @@ -90,8 +96,8 @@ relegated static char *AddFlag(char *p, int b, const char *s) { return p; } -relegated static char *DescribeCpuFlags(char *p, int flags, int x87sw, - int mxcsr) { +relegated static dontinline char *DescribeCpuFlags(char *p, int flags, + int x87sw, int mxcsr) { unsigned i; for (i = 0; i < ARRAYLEN(kCpuFlags); ++i) { if (flags & 1) { @@ -123,38 +129,16 @@ static char *HexCpy(char p[hasatleast 17], uint64_t x, uint8_t k) { } relegated static char *ShowGeneralRegisters(char *p, ucontext_t *ctx) { - int64_t x; + size_t i, j; const char *s; - size_t i, j, k; - long double st; *p++ = '\n'; - for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { + for (i = 0, j = 0; i < ARRAYLEN(kGregNames); ++i) { if (j > 0) *p++ = ' '; if (!(s = kGregNames[(unsigned)kGregOrder[i]])[2]) *p++ = ' '; p = stpcpy(p, s), *p++ = ' '; p = HexCpy(p, ctx->uc_mcontext.gregs[(unsigned)kGregOrder[i]], 64); if (++j == 3) { j = 0; - if (ctx->uc_mcontext.fpregs) { - memcpy(&st, (char *)&ctx->uc_mcontext.fpregs->st[k], sizeof(st)); - p = stpcpy(p, " ST("); - p = FormatUint64(p, k++); - p = stpcpy(p, ") "); - if (signbit(st)) { - st = -st; - *p++ = '-'; - } - if (isnan(st)) { - p = stpcpy(p, "nan"); - } else if (isinf(st)) { - p = stpcpy(p, "inf"); - } else { - if (st > 999.999) st = 999.999; - x = st * 1000; - p = FormatUint64(p, x / 1000), *p++ = '.'; - p = FormatUint64(p, x % 1000); - } - } *p++ = '\n'; } } @@ -203,12 +187,22 @@ relegated static char *ShowSseRegisters(char *p, ucontext_t *ctx) { void ShowCrashReportHook(int, int, int, struct siginfo *, ucontext_t *); -relegated void ShowCrashReport(int err, int sig, struct siginfo *si, - ucontext_t *ctx) { +static relegated void ShowCrashReport(int err, int sig, struct siginfo *si, + ucontext_t *ctx) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + long size = __get_safe_size(8192, 4096); + if (size < 6000) { + klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); + __minicrash(sig, si, ctx); + return; + } + char *buf = alloca(size); + CheckLargeStackAllocation(buf, size); +#pragma GCC pop_options int i; - char *p; + char *p = buf; char host[64]; - char buf[3000]; struct utsname names; if (_weaken(ShowCrashReportHook)) { ShowCrashReportHook(2, err, sig, si, ctx); @@ -222,21 +216,18 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, uname(&names); errno = err; // TODO(jart): Buffer the WHOLE crash report with backtrace for atomic write. - p = buf; - p += ksnprintf( - p, 10000, - "\n%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n" - " %s\n" - " %s\n" - " %s %s %s %s\n", - !__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "", sig, - (ctx && - (ctx->uc_mcontext.rsp >= GetStaticStackAddr(0) && - ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + getauxval(AT_PAGESZ))) - ? "Stack Overflow" - : DescribeSiCode(sig, si->si_code), - host, getpid(), gettid(), program_invocation_name, strerror(err), - names.sysname, names.version, names.nodename, names.release); + p += + ksnprintf(p, 4000, + "\n%serror%s: Uncaught %G (%s) at %p on %s pid %d tid %d\n" + " %s\n" + " %s\n" + " %s %s %s %s\n", + !__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "", sig, + __is_stack_overflow(si, ctx) ? "\e[7mStack Overflow\e[0m" + : DescribeSiCode(sig, si->si_code), + si->si_addr, host, getpid(), gettid(), program_invocation_name, + strerror(err), names.sysname, names.version, names.nodename, + names.release); if (ctx) { p = ShowGeneralRegisters(p, ctx); p = ShowSseRegisters(p, ctx); @@ -248,45 +239,21 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, klog(buf, p - buf); } kprintf("\n"); - if (!IsWindows()) __print_maps(); + if (!IsWindows()) { + __print_maps(); + } /* PrintSystemMappings(2); */ if (__argv) { for (i = 0; i < __argc; ++i) { - if (!__argv[i]) continue; - if (IsAsan() && !__asan_is_valid_str(__argv[i])) continue; kprintf("%s ", __argv[i]); } } kprintf("\n"); } -static relegated wontreturn void RaiseCrash(int sig) { - sigset_t ss; - sigfillset(&ss); - sigdelset(&ss, sig); - sigprocmask(SIG_SETMASK, &ss, 0); - signal(sig, SIG_DFL); - kill(getpid(), sig); - _Exit(128 + sig); -} - -relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) { - int gdbpid, err; +relegated void __oncrash(int sig, struct siginfo *si, void *arg) { ucontext_t *ctx = arg; - - // print vital error nubers reliably - // the surface are of code this calls is small and audited - kprintf( - "\r\n\e[1;31m__oncrash %G %s pid %d tid %d rip %x bt %s\e[0m\n", sig, - program_invocation_short_name, getpid(), sys_gettid(), - ctx ? ctx->uc_mcontext.rip : 0, - DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.rbp - : (struct StackFrame *)__builtin_frame_address(0))); - - // print friendlier detailed crash report less reliably - // we're in a broken runtime state and so much can go wrong - ftrace_enabled(-1); - strace_enabled(-1); + int gdbpid, err; err = errno; if ((gdbpid = IsDebuggerPresent(true))) { DebugBreak(); @@ -294,7 +261,6 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) { if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) { __restore_tty(); ShowCrashReport(err, sig, si, ctx); - RaiseCrash(sig); } } diff --git a/libc/log/oncrash_arm64.c b/libc/log/oncrash_arm64.c index 82578906f..1ab357367 100644 --- a/libc/log/oncrash_arm64.c +++ b/libc/log/oncrash_arm64.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" #include "libc/assert.h" +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/aarch64.internal.h" #include "libc/calls/struct/rusage.internal.h" @@ -25,10 +26,12 @@ #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/struct/utsname.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/ucontext.h" #include "libc/errno.h" +#include "libc/intrin/describebacktrace.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/log/internal.h" @@ -36,6 +39,7 @@ #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" @@ -49,6 +53,8 @@ __static_yoink("strerror_wr"); // for kprintf %m __static_yoink("strsignal_r"); // for kprintf %G +#define STACK_ERROR "error: not enough room on stack to print crash report\n" + #define RESET "\e[0m" #define BOLD "\e[1m" #define STRONG "\e[30;101m" @@ -75,16 +81,6 @@ static relegated void Append(struct Buffer *b, const char *fmt, ...) { va_end(va); } -static relegated wontreturn void RaiseCrash(int sig) { - sigset_t ss; - sigfillset(&ss); - sigdelset(&ss, sig); - sigprocmask(SIG_SETMASK, &ss, 0); - signal(sig, SIG_DFL); - kill(getpid(), sig); - _Exit(128 + sig); -} - static relegated const char *ColorRegister(int r) { if (__nocolor) return ""; switch (r) { @@ -144,7 +140,7 @@ static relegated bool AppendFileLine(struct Buffer *b, const char *addr2line, sys_close(pfd[0]); sys_dup2(pfd[1], 1, 0); sys_close(2); - __sys_execve( + sys_execve( addr2line, (char *const[]){(char *)addr2line, "-pifCe", (char *)debugbin, buf, 0}, (char *const[]){0}); @@ -189,98 +185,129 @@ static relegated char *GetSymbolName(struct SymbolTable *st, int symbol, return s; } -relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) { - char buf[10000]; - ucontext_t *ctx = arg; - static _Thread_local bool once; - struct Buffer b[1] = {{buf, sizeof(buf)}}; - b->p[b->i++] = '\n'; - if (!once) { - int i, j; - const char *kind; - const char *reset; - const char *strong; - char host[64] = "unknown"; - struct utsname names = {0}; - once = true; - ftrace_enabled(-1); - strace_enabled(-1); - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); +static relegated void __oncrash_impl(int sig, struct siginfo *si, + ucontext_t *ctx) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + long size = __get_safe_size(10000, 4096); + if (size < 80) { + // almost certainly guaranteed to succeed + klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); __restore_tty(); - uname(&names); - gethostname(host, sizeof(host)); - reset = !__nocolor ? RESET : ""; - strong = !__nocolor ? STRONG : ""; - if (ctx && - (ctx->uc_mcontext.sp & (GetStackSize() - 1)) <= getauxval(AT_PAGESZ)) { - kind = "Stack Overflow"; - } else { - kind = DescribeSiCode(sig, si->si_code); - } - Append(b, - "%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n" - "%s\n", - strong, reset, sig, kind, host, getpid(), gettid(), - program_invocation_name); - if (errno) { - Append(b, " %m\n"); - } - Append(b, " %s %s %s %s\n", names.sysname, names.version, names.nodename, - names.release); - if (ctx) { - long pc; - char *mem = 0; - size_t memsz = 0; - int addend, symbol; - const char *debugbin; - const char *addr2line; - struct StackFrame *fp; - struct SymbolTable *st; - struct fpsimd_context *vc; - st = GetSymbolTable(); - debugbin = FindDebugBinary(); - addr2line = GetAddr2linePath(); + __minicrash(sig, si, ctx); + return; + } + char *buf = alloca(size); + CheckLargeStackAllocation(buf, size); +#pragma GCC pop_options + int i, j; + const char *kind; + const char *reset; + const char *strong; + char host[64] = "unknown"; + struct utsname names = {0}; + struct Buffer b[1] = {{buf, size}}; + b->p[b->i++] = '\n'; + ftrace_enabled(-1); + strace_enabled(-1); + __restore_tty(); + uname(&names); + gethostname(host, sizeof(host)); + reset = !__nocolor ? RESET : ""; + strong = !__nocolor ? STRONG : ""; + if (__is_stack_overflow(si, ctx)) { + kind = "\e[7mStack Overflow\e[0m"; + } else { + kind = DescribeSiCode(sig, si->si_code); + } + Append(b, "%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n", strong, reset, + sig, kind, host, getpid(), gettid()); + if (program_invocation_name) { + Append(b, " %s\n", program_invocation_name); + } + if (errno) { + Append(b, " %s\n", strerror(errno)); + } + Append(b, " %s %s %s %s\n", names.sysname, names.version, names.nodename, + names.release); + if (ctx) { + long pc; + char *mem = 0; + size_t memsz = 0; + int addend, symbol; + const char *debugbin; + const char *addr2line; + struct StackFrame *fp; + struct SymbolTable *st; + struct fpsimd_context *vc; + st = GetSymbolTable(); + debugbin = FindDebugBinary(); + addr2line = GetAddr2linePath(); - if (ctx->uc_mcontext.fault_address) { - Append(b, " fault_address = %#lx\n", ctx->uc_mcontext.fault_address); + if (sig == SIGFPE || // + sig == SIGILL || // + sig == SIGBUS || // + sig == SIGSEGV || // + sig == SIGTRAP) { + Append(b, " faulting address is %016lx\n", si->si_addr); + } + + // PRINT REGISTERS + for (i = 0; i < 8; ++i) { + Append(b, " "); + for (j = 0; j < 4; ++j) { + int r = 8 * j + i; + if (j) Append(b, " "); + Append(b, "%s%016lx%s x%d%s", ColorRegister(r), + ctx->uc_mcontext.regs[r], reset, r, r == 8 || r == 9 ? " " : ""); } + Append(b, "\n"); + } - // PRINT REGISTERS - for (i = 0; i < 8; ++i) { + // PRINT VECTORS + vc = (struct fpsimd_context *)ctx->uc_mcontext.__reserved; + if (vc->head.magic == FPSIMD_MAGIC) { + int n = 16; + while (n && !vc->vregs[n - 1] && !vc->vregs[n - 2]) n -= 2; + for (i = 0; i * 2 < n; ++i) { Append(b, " "); - for (j = 0; j < 4; ++j) { - int r = 8 * j + i; + for (j = 0; j < 2; ++j) { + int r = j + 2 * i; if (j) Append(b, " "); - Append(b, "%s%016lx%s x%d%s", ColorRegister(r), - ctx->uc_mcontext.regs[r], reset, r, - r == 8 || r == 9 ? " " : ""); + Append(b, "%016lx ..%s %016lx v%d%s", (long)(vc->vregs[r] >> 64), + !j ? "" : ".", (long)vc->vregs[r], r, r < 10 ? " " : ""); } Append(b, "\n"); } + } - // PRINT VECTORS - vc = (struct fpsimd_context *)ctx->uc_mcontext.__reserved; - if (vc->head.magic == FPSIMD_MAGIC) { - int n = 16; - while (n && !vc->vregs[n - 1] && !vc->vregs[n - 2]) n -= 2; - for (i = 0; i * 2 < n; ++i) { - Append(b, " "); - for (j = 0; j < 2; ++j) { - int r = j + 2 * i; - if (j) Append(b, " "); - Append(b, "%016lx ..%s %016lx v%d%s", (long)(vc->vregs[r] >> 64), - !j ? "" : ".", (long)vc->vregs[r], r, r < 10 ? " " : ""); - } - Append(b, "\n"); - } + // PRINT CURRENT LOCATION + // + // We can get the address of the currently executing function by + // simply examining the program counter. + pc = ctx->uc_mcontext.pc; + Append(b, " %016lx sp %lx pc", ctx->uc_mcontext.sp, pc); + if (pc && st && (symbol = __get_symbol(st, pc))) { + addend = pc - st->addr_base; + addend -= st->symbols[symbol].x; + Append(b, " "); + if (!AppendFileLine(b, addr2line, debugbin, pc)) { + Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz)); + if (addend) Append(b, "%+d", addend); } + } + Append(b, "\n"); - // PRINT CURRENT LOCATION - // - // We can get the address of the currently executing function by - // simply examining the program counter. - pc = ctx->uc_mcontext.pc; - Append(b, " %016lx sp %lx pc", ctx->uc_mcontext.sp, pc); + // PRINT LINKED LOCATION + // + // The x30 register can usually tell us the address of the parent + // function. This can help us determine the caller in cases where + // stack frames aren't being generated by the compiler; but if we + // have stack frames, then we need to ensure this won't duplicate + // the first element of the frame pointer backtrace below. + fp = (struct StackFrame *)ctx->uc_mcontext.regs[29]; + if (IsCode((pc = ctx->uc_mcontext.regs[30]))) { + Append(b, " %016lx sp %lx lr", ctx->uc_mcontext.sp, pc); if (pc && st && (symbol = __get_symbol(st, pc))) { addend = pc - st->addr_base; addend -= st->symbols[symbol].x; @@ -291,75 +318,56 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) { } } Append(b, "\n"); + if (fp && !kisdangerous(fp) && pc == fp->addr) { + fp = fp->next; + } + } - // PRINT LINKED LOCATION - // - // The x30 register can usually tell us the address of the parent - // function. This can help us determine the caller in cases where - // stack frames aren't being generated by the compiler; but if we - // have stack frames, then we need to ensure this won't duplicate - // the first element of the frame pointer backtrace below. - fp = (struct StackFrame *)ctx->uc_mcontext.regs[29]; - if (IsCode((pc = ctx->uc_mcontext.regs[30]))) { - Append(b, " %016lx sp %lx lr", ctx->uc_mcontext.sp, pc); - if (pc && st && (symbol = __get_symbol(st, pc))) { + // PRINT FRAME POINTERS + // + // The prologues and epilogues of non-leaf functions should save + // the frame pointer (x29) and return address (x30) to the stack + // and then set x29 to sp, which is the address of the new frame + // effectively creating a daisy chain letting us trace back into + // the origin of execution, e.g. _start(), or sys_clone_linux(). + for (i = 0; fp; fp = fp->next) { + if (kisdangerous(fp)) { + Append(b, " %016lx \n", fp); + break; + } + if (++i == 100) { + Append(b, " \n"); + break; + } + if (st && (pc = fp->addr)) { + if ((symbol = __get_symbol(st, pc))) { addend = pc - st->addr_base; addend -= st->symbols[symbol].x; - Append(b, " "); - if (!AppendFileLine(b, addr2line, debugbin, pc)) { - Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz)); - if (addend) Append(b, "%+d", addend); - } - } - Append(b, "\n"); - if (fp && !kisdangerous(fp) && pc == fp->addr) { - fp = fp->next; - } - } - - // PRINT FRAME POINTERS - // - // The prologues and epilogues of non-leaf functions should save - // the frame pointer (x29) and return address (x30) to the stack - // and then set x29 to sp, which is the address of the new frame - // effectively creating a daisy chain letting us trace back into - // the origin of execution, e.g. _start(), or sys_clone_linux(). - for (i = 0; fp; fp = fp->next) { - if (kisdangerous(fp)) { - Append(b, " %016lx \n", fp); - break; - } - if (++i == 100) { - Append(b, " \n"); - break; - } - if (st && (pc = fp->addr)) { - if ((symbol = __get_symbol(st, pc))) { - addend = pc - st->addr_base; - addend -= st->symbols[symbol].x; - } else { - addend = 0; - } } else { - symbol = 0; addend = 0; } - Append(b, " %016lx fp %lx lr ", fp, pc); - if (!AppendFileLine(b, addr2line, debugbin, pc) && st) { - Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz)); - if (addend) Append(b, "%+d", addend); - } - Append(b, "\n"); + } else { + symbol = 0; + addend = 0; } - free(mem); + Append(b, " %016lx fp %lx lr ", fp, pc); + if (!AppendFileLine(b, addr2line, debugbin, pc) && st) { + Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz)); + if (addend) Append(b, "%+d", addend); + } + Append(b, "\n"); } - } else { - Append(b, "got %G while crashing! pc %lx lr %lx\n", sig, - ctx->uc_mcontext.pc, ctx->uc_mcontext.regs[30]); + free(mem); } + b->p[b->n - 1] = '\n'; sys_write(2, b->p, MIN(b->i, b->n)); - __print_maps(); - RaiseCrash(sig); +} + +relegated void __oncrash(int sig, struct siginfo *si, void *arg) { + ucontext_t *ctx = arg; + BLOCK_CANCELLATIONS; + __oncrash_impl(sig, si, ctx); + ALLOW_CANCELLATIONS; } #endif /* __aarch64__ */ diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index fdb9b35af..095e79f6c 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -17,26 +17,18 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" -#include "libc/dce.h" -#include "libc/errno.h" +#include "libc/calls/struct/sigset.h" +#include "libc/intrin/leaky.internal.h" #include "libc/log/internal.h" -#include "libc/log/log.h" -#include "libc/macros.internal.h" #include "libc/mem/mem.h" -#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/map.h" -#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/ss.h" +#ifndef TINY __static_yoink("zipos"); // for symtab __static_yoink("__die"); // for backtracing __static_yoink("ShowBacktrace"); // for backtracing @@ -44,82 +36,26 @@ __static_yoink("GetSymbolTable"); // for backtracing __static_yoink("PrintBacktraceUsingSymbols"); // for backtracing __static_yoink("malloc_inspect_all"); // for asan memory origin __static_yoink("GetSymbolByAddr"); // for asan memory origin - -struct CrashHandler { - int sig; - struct sigaction old; -}; - -static inline void __oncrash(int sig, struct siginfo *si, void *arg) { -#ifdef __x86_64__ - __oncrash_amd64(sig, si, arg); -#elif defined(__aarch64__) - __oncrash_arm64(sig, si, arg); -#else - abort(); #endif -} -static void __got_sigquit(int sig, struct siginfo *si, void *arg) { - write(2, "^\\", 2); - __oncrash(sig, si, arg); -} -static void __got_sigfpe(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigill(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigsegv(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigtrap(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigabrt(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigbus(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} -static void __got_sigurg(int sig, struct siginfo *si, void *arg) { - __oncrash(sig, si, arg); -} - -static void RemoveCrashHandler(void *arg) { - int e; - struct CrashHandler *ch = arg; - strace_enabled(-1); - e = errno; - sigaction(ch->sig, &ch->old, NULL); - errno = e; - free(ch); - strace_enabled(+1); -} - -static void InstallCrashHandler(int sig, sigaction_f thunk) { - int e; +static void InstallCrashHandler(int sig, int flags) { struct sigaction sa; - struct CrashHandler *ch; - e = errno; - if ((ch = malloc(sizeof(*ch)))) { - ch->sig = sig; - sa.sa_sigaction = thunk; - sigfillset(&sa.sa_mask); - sigdelset(&sa.sa_mask, SIGQUIT); - sigdelset(&sa.sa_mask, SIGFPE); - sigdelset(&sa.sa_mask, SIGILL); - sigdelset(&sa.sa_mask, SIGSEGV); - sigdelset(&sa.sa_mask, SIGTRAP); - sigdelset(&sa.sa_mask, SIGABRT); - sigdelset(&sa.sa_mask, SIGBUS); - sigdelset(&sa.sa_mask, SIGURG); - sa.sa_flags = SA_SIGINFO | SA_ONSTACK; - if (!sigaction(sig, &sa, &ch->old)) { - __cxa_atexit(RemoveCrashHandler, ch, 0); - } - } - errno = e; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGFPE); + sigaddset(&sa.sa_mask, SIGILL); + sigaddset(&sa.sa_mask, SIGSEGV); + sigaddset(&sa.sa_mask, SIGTRAP); + sigaddset(&sa.sa_mask, SIGBUS); + sigaddset(&sa.sa_mask, SIGABRT); + sa.sa_flags = SA_SIGINFO | flags; +#ifdef TINY + sa.sa_sigaction = __minicrash; +#else + GetSymbolTable(); + sa.sa_sigaction = __oncrash; +#endif + unassert(!sigaction(sig, &sa, 0)); } /** @@ -138,21 +74,24 @@ static void InstallCrashHandler(int sig, sigaction_f thunk) { * useful, for example, if a program is caught in an infinite loop. */ void ShowCrashReports(void) { - if (!IsWindows()) { - struct sigaltstack ss; - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - ss.ss_sp = malloc(SIGSTKSZ); - sigaltstack(&ss, 0); - __cxa_atexit(free, ss.ss_sp, 0); - } - InstallCrashHandler(SIGQUIT, __got_sigquit); // ctrl+\ aka ctrl+break - InstallCrashHandler(SIGFPE, __got_sigfpe); // 1 / 0 - InstallCrashHandler(SIGILL, __got_sigill); // illegal instruction - InstallCrashHandler(SIGSEGV, __got_sigsegv); // bad memory access - InstallCrashHandler(SIGTRAP, __got_sigtrap); // bad system call - InstallCrashHandler(SIGBUS, __got_sigbus); // misalign, mmap i/o failed - InstallCrashHandler(SIGURG, __got_sigurg); // placeholder + struct sigaltstack ss; + static char crashstack[65536]; + ss.ss_flags = 0; + ss.ss_size = sizeof(crashstack); + ss.ss_sp = crashstack; + unassert(!sigaltstack(&ss, 0)); + InstallCrashHandler(SIGQUIT, 0); +#ifdef __x86_64__ + InstallCrashHandler(SIGTRAP, 0); +#else + InstallCrashHandler(SIGTRAP, SA_RESETHAND); +#endif + InstallCrashHandler(SIGFPE, SA_RESETHAND); + InstallCrashHandler(SIGILL, SA_RESETHAND); + InstallCrashHandler(SIGBUS, SA_RESETHAND); + InstallCrashHandler(SIGABRT, SA_RESETHAND); + InstallCrashHandler(SIGSEGV, SA_RESETHAND | SA_ONSTACK); _wantcrashreports = true; - GetSymbolTable(); } + +IGNORE_LEAKS(ShowCrashReports) diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index a0143fc43..56ce1f0aa 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -136,7 +136,6 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, "exiting due to aforementioned error (host %s pid %d tid %d)\n", buf32, getpid(), gettid()); __die(); - __builtin_unreachable(); } ALLOW_CANCELLATIONS; diff --git a/libc/mem/gc.c b/libc/mem/gc.c index b7ad3be74..fc3ab1581 100644 --- a/libc/mem/gc.c +++ b/libc/mem/gc.c @@ -62,7 +62,6 @@ static void DeferFunction(struct StackFrame *frame, void *fn, void *arg) { struct Garbage *p2; struct Garbages *g; struct CosmoTib *t; - __require_tls(); t = __get_tls(); g = t->tib_garbages; if (UNLIKELY(!g)) { diff --git a/libc/mem/mem.mk b/libc/mem/mem.mk index 2cc9b3cc0..8f25a042c 100644 --- a/libc/mem/mem.mk +++ b/libc/mem/mem.mk @@ -37,6 +37,12 @@ $(LIBC_MEM_A).pkg: \ $(LIBC_MEM_A_OBJS) \ $(foreach x,$(LIBC_MEM_A_DIRECTDEPS),$($(x)_A).pkg) +$(LIBC_MEM_A_OBJS): private \ + COPTS += \ + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + LIBC_MEM_LIBS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x))) LIBC_MEM_SRCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_SRCS)) LIBC_MEM_HDRS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/nexgen32e/crc32.h b/libc/nexgen32e/crc32.h index ea44aad67..b87ae189c 100644 --- a/libc/nexgen32e/crc32.h +++ b/libc/nexgen32e/crc32.h @@ -6,8 +6,7 @@ COSMOPOLITAN_C_START_ extern const uint32_t kCrc32cTab[256]; void crc32init(uint32_t[hasatleast 256], uint32_t); -uint32_t crc32a(uint32_t, const void *, size_t); -uint32_t crc32c(uint32_t, const void *, size_t); +uint32_t crc32c(uint32_t, const void *, size_t) nosideeffect; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/nexgen32e/gc.S b/libc/nexgen32e/gc.S index 6be8f149f..f86f5eea3 100644 --- a/libc/nexgen32e/gc.S +++ b/libc/nexgen32e/gc.S @@ -66,7 +66,9 @@ __gc: .ftrace2 #elif defined(__aarch64__) - sub x8,x28,#1152 // __get_tls() +// if this code fails +// check if CosmoTib's size changed + sub x8,x28,#128 // __get_tls() ldr x9,[x8,0x18] // tib::garbages ldr x10,[x9] // g->i ldr x8,[x9,8] // g->p diff --git a/libc/nexgen32e/stackframe.h b/libc/nexgen32e/stackframe.h index 24435f761..04bd1e41e 100644 --- a/libc/nexgen32e/stackframe.h +++ b/libc/nexgen32e/stackframe.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_NEXGEN32E_STACKFRAME_H_ #define COSMOPOLITAN_LIBC_NEXGEN32E_STACKFRAME_H_ +#ifdef _COSMO_SOURCE #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -10,4 +11,5 @@ struct StackFrame { COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* _COSMO_SOURCE */ #endif /* COSMOPOLITAN_LIBC_NEXGEN32E_STACKFRAME_H_ */ diff --git a/libc/nt/enum/accessmask.h b/libc/nt/enum/accessmask.h index da67f0682..d2d0fdaa6 100644 --- a/libc/nt/enum/accessmask.h +++ b/libc/nt/enum/accessmask.h @@ -1,25 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_NT_ENUM_ACCESSMASK_H_ #define COSMOPOLITAN_LIBC_NT_ENUM_ACCESSMASK_H_ -/** - * Can also be described as follows: - * - * struct NtAccessMask { - * union { - * uint32_t value; - * struct { - * uint16_t SpecificRights; - * unsigned char StandardRights; - * unsigned AccessSystemAcl : 1; - * unsigned Reserved : 3; - * unsigned GenericAll : 1; - * unsigned GenericExecute : 1; - * unsigned GenericWrite : 1; - * unsigned GenericRead : 1; - * }; - * }; - * }; - */ #define kNtGenericRead 0x80000000u #define kNtGenericWrite 0x40000000u #define kNtGenericExecute 0x20000000u diff --git a/libc/nt/enum/processcreationflags.h b/libc/nt/enum/processcreationflags.h index e0ff5403f..a6a6d74ac 100644 --- a/libc/nt/enum/processcreationflags.h +++ b/libc/nt/enum/processcreationflags.h @@ -33,4 +33,6 @@ #define kNtProfileServer 0x40000000u #define kNtCreateIgnoreSystemDefault 0x80000000u +#define kNtStackSizeParamIsAReservation 0x00010000 + #endif /* COSMOPOLITAN_LIBC_NT_ENUM_PROCESSCREATIONFLAGS_H_ */ diff --git a/libc/nt/files.h b/libc/nt/files.h index 65a4d9955..7a700bdb1 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -137,9 +137,6 @@ bool32 CreateSymbolicLink(const char16_t *lpSymlinkFileName, const char16_t *lpTargetPathName, uint32_t dwFlags) paramsnonnull(); -uint32_t SetFilePointer(int64_t hFile, int32_t lDistanceToMove, - int32_t *optional_lpDistanceToMoveHigh, - int dwMoveMethod); bool32 SetFilePointerEx(int64_t hFile, int64_t liDistanceToMove, int64_t *optional_lpNewFilePointer, int dwMoveMethod); diff --git a/libc/nt/kernel32/GetCurrentProcess.S b/libc/nt/kernel32/GetCurrentProcess.S deleted file mode 100644 index 381cbd3d1..000000000 --- a/libc/nt/kernel32/GetCurrentProcess.S +++ /dev/null @@ -1,19 +0,0 @@ -#include "libc/nt/codegen.h" -.imp kernel32,__imp_GetCurrentProcess,GetCurrentProcess - - .text.windows - .ftrace1 -GetCurrentProcess: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - sub $32,%rsp - call *__imp_GetCurrentProcess(%rip) - leave -#elif defined(__aarch64__) - mov x0,#0 -#endif - ret - .endfn GetCurrentProcess,globl - .previous diff --git a/libc/nt/kernel32/SetFilePointer.S b/libc/nt/kernel32/SetFilePointer.S deleted file mode 100644 index 4fc250d78..000000000 --- a/libc/nt/kernel32/SetFilePointer.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp kernel32,__imp_SetFilePointer,SetFilePointer - - .text.windows - .ftrace1 -SetFilePointer: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_SetFilePointer(%rip),%rax - jmp __sysv2nt -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn SetFilePointer,globl - .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 089ce1110..4af5999d0 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -104,7 +104,6 @@ imp 'GetConsoleSelectionInfo' GetConsoleSelectionInfo kernel32 1 imp 'GetConsoleTitle' GetConsoleTitleW kernel32 2 imp 'GetConsoleWindow' GetConsoleWindow kernel32 0 imp 'GetCurrentDirectory' GetCurrentDirectoryW kernel32 2 -imp 'GetCurrentProcess' GetCurrentProcess kernel32 0 imp 'GetCurrentProcessId' GetCurrentProcessId kernel32 0 imp 'GetCurrentThread' GetCurrentThread kernel32 0 imp 'GetCurrentThreadId' GetCurrentThreadId kernel32 0 @@ -245,7 +244,6 @@ imp 'SetErrorMode' SetErrorMode kernel32 1 imp 'SetEvent' SetEvent kernel32 1 imp 'SetFileAttributes' SetFileAttributesW kernel32 2 imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 2 -imp 'SetFilePointer' SetFilePointer kernel32 4 imp 'SetFilePointerEx' SetFilePointerEx kernel32 4 imp 'SetFileTime' SetFileTime kernel32 4 imp 'SetFileValidData' SetFileValidData kernel32 2 diff --git a/libc/nt/runtime.h b/libc/nt/runtime.h index 54dc26b73..cd2902bfa 100644 --- a/libc/nt/runtime.h +++ b/libc/nt/runtime.h @@ -18,6 +18,8 @@ #define kNtStdOutputHandle -11u #define kNtStdErrorHandle -12u +#define GetCurrentProcess() -1 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -32,7 +34,7 @@ bool32 WriteFile(int64_t hFile, const void *lpBuffer, uint32_t *lpNumberOfBytesWritten, struct NtOverlapped *opt_lpOverlapped); bool32 TerminateProcess(int64_t hProcess, uint32_t uExitCode); -int64_t GetCurrentProcess(void) pureconst; +void TerminateThisProcess(uint32_t dwWaitStatus) wontreturn; void ExitProcess(uint32_t uExitCode) wontreturn; uint32_t GetLastError(void) nosideeffect; bool32 CloseHandle(int64_t hObject) dontthrow nocallback; diff --git a/libc/nt/struct/overlapped.h b/libc/nt/struct/overlapped.h index a957ea5c7..7580ddbf8 100644 --- a/libc/nt/struct/overlapped.h +++ b/libc/nt/struct/overlapped.h @@ -10,7 +10,7 @@ struct NtOverlapped { uint32_t Offset; uint32_t OffsetHigh; }; - void *Pointer; + int64_t Pointer; }; int64_t hEvent; }; diff --git a/libc/nt/thread.h b/libc/nt/thread.h index 2ede7f3b3..6570321cf 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -34,7 +34,7 @@ COSMOPOLITAN_C_START_ int64_t CreateThread(const struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize, void *lpStartAddress, void *lpParameter, uint32_t dwCreationFlags, - uint32_t *opt_lpThreadId); + uint32_t *opt_lpThreadId) paramsnonnull((3)); void ExitThread(uint32_t dwExitCode) wontreturn; int64_t GetCurrentThread(void); diff --git a/libc/nt/thunk/runtime.inc b/libc/nt/thunk/runtime.inc index c5ccfec09..3591856b1 100644 --- a/libc/nt/thunk/runtime.inc +++ b/libc/nt/thunk/runtime.inc @@ -26,9 +26,6 @@ extern typeof(WriteFile) *const __imp_WriteFile __msabi; extern typeof(SetDefaultDllDirectories) *const __imp_SetDefaultDllDirectories __msabi; -#define GetCurrentProcess(...) __imp_GetCurrentProcess(__VA_ARGS__) -extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess __msabi; - #define GetModuleFileName(...) __imp_GetModuleFileNameW(__VA_ARGS__) extern typeof(GetModuleFileName) *const __imp_GetModuleFileNameW __msabi; diff --git a/libc/runtime/cocmd.c b/libc/proc/cocmd.c similarity index 99% rename from libc/runtime/cocmd.c rename to libc/proc/cocmd.c index 507951307..4d73b676c 100644 --- a/libc/runtime/cocmd.c +++ b/libc/proc/cocmd.c @@ -365,7 +365,7 @@ static int Mktemp(void) { if (n == 2) { strlcpy(template, args[1], sizeof(template)); } else { - strlcpy(template, kTmpPath, sizeof(template)); + strlcpy(template, __get_tmpdir(), sizeof(template)); strlcat(template, "tmp.XXXXXX", sizeof(template)); } if ((fd = mkstemp(template)) == -1) { diff --git a/libc/runtime/daemon.c b/libc/proc/daemon.c similarity index 100% rename from libc/runtime/daemon.c rename to libc/proc/daemon.c diff --git a/libc/calls/execl.c b/libc/proc/execl.c similarity index 88% rename from libc/calls/execl.c rename to libc/proc/execl.c index b634eae2a..b9c1cf476 100644 --- a/libc/calls/execl.c +++ b/libc/proc/execl.c @@ -19,6 +19,8 @@ #include "libc/calls/calls.h" #include "libc/mem/alloca.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" +#include "libc/sysv/errfuns.h" /** * Executes program, with current environment. @@ -35,13 +37,18 @@ */ int execl(const char *exe, const char *arg, ... /*, NULL*/) { int i; - char **argv; va_list va, vb; va_copy(vb, va); va_start(va, arg); for (i = 0; va_arg(va, const char *); ++i) donothing; va_end(va); - argv = alloca((i + 2) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (i + 2) * sizeof(char *); + if (__get_safe_size(nbytes, 4096) < nbytes) return enomem(); + char **argv = alloca(nbytes); + CheckLargeStackAllocation(argv, nbytes); +#pragma GCC pop_options va_start(vb, arg); argv[0] = (char *)arg; for (i = 1;; ++i) { diff --git a/libc/calls/execle.c b/libc/proc/execle.c similarity index 88% rename from libc/calls/execle.c rename to libc/proc/execle.c index eb48bc8d6..54773966a 100644 --- a/libc/calls/execle.c +++ b/libc/proc/execle.c @@ -18,6 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/mem/alloca.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" +#include "libc/sysv/errfuns.h" /** * Executes program, with custom environment. @@ -43,7 +46,13 @@ int execle(const char *exe, const char *arg, for (i = 0; va_arg(va, const char *); ++i) donothing; envp = va_arg(va, char **); va_end(va); - argv = alloca((i + 2) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (i + 2) * sizeof(char *); + if (__get_safe_size(nbytes, 4096) < nbytes) return enomem(); + argv = alloca(nbytes); + CheckLargeStackAllocation(argv, nbytes); +#pragma GCC pop_options va_start(vb, arg); argv[0] = (char *)arg; for (i = 1;; ++i) { diff --git a/libc/calls/execlp.c b/libc/proc/execlp.c similarity index 91% rename from libc/calls/execlp.c rename to libc/proc/execlp.c index c0b5b0523..3b2967489 100644 --- a/libc/calls/execlp.c +++ b/libc/proc/execlp.c @@ -20,6 +20,7 @@ #include "libc/limits.h" #include "libc/mem/alloca.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" @@ -48,7 +49,13 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) { va_start(va, arg); for (i = 0; va_arg(va, const char *); ++i) (void)0; va_end(va); - argv = alloca((i + 2) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (i + 2) * sizeof(char *); + if (__get_safe_size(nbytes, 4096) < nbytes) return enomem(); + argv = alloca(nbytes); + CheckLargeStackAllocation(argv, nbytes); +#pragma GCC pop_options va_start(vb, arg); argv[0] = (char *)arg; for (i = 1;; ++i) { diff --git a/libc/calls/execv.c b/libc/proc/execv.c similarity index 100% rename from libc/calls/execv.c rename to libc/proc/execv.c diff --git a/libc/calls/execve-nt.greg.c b/libc/proc/execve-nt.greg.c similarity index 64% rename from libc/calls/execve-nt.greg.c rename to libc/proc/execve-nt.greg.c index cb3ec3749..8eaa45661 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/proc/execve-nt.greg.c @@ -18,57 +18,46 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/ntspawn.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/sigaction.internal.h" -#include "libc/calls/syscall-nt.internal.h" -#include "libc/dce.h" #include "libc/fmt/itoa.h" -#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" -#include "libc/intrin/strace.internal.h" +#include "libc/intrin/handlock.internal.h" #include "libc/intrin/weaken.h" -#include "libc/limits.h" -#include "libc/macros.internal.h" -#include "libc/mem/alloca.h" #include "libc/nt/accounting.h" #include "libc/nt/console.h" #include "libc/nt/enum/startf.h" #include "libc/nt/enum/status.h" -#include "libc/nt/enum/threadaccess.h" #include "libc/nt/enum/wait.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" -#include "libc/nt/process.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/msg.h" #include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/startupinfo.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" -#include "libc/runtime/internal.h" +#include "libc/proc/ntspawn.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" #include "libc/sock/sock.h" #include "libc/str/str.h" -#include "libc/sysv/consts/at.h" -#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/itimer.internal.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" +#include "libc/thread/tls.h" #ifdef __x86_64__ -#define keywords textwindows dontasan dontubsan dontinstrument +#define keywords textwindows dontinstrument // clang-format off __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; __msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle; -__msabi extern typeof(ExitProcess) *const __imp_ExitProcess; __msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent; __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; __msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; @@ -83,9 +72,9 @@ __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; extern long __klog_handle; static void sys_execve_nt_relay(intptr_t, long, long, long); -wontreturn void __switch_stacks(intptr_t, long, long, long, - void (*)(intptr_t, intptr_t, long, long), - intptr_t); +void __stack_call(intptr_t, long, long, long, + void (*)(intptr_t, intptr_t, long, long), + intptr_t) wontreturn; static keywords void PurgeHandle(intptr_t h) { if (!h) return; @@ -100,80 +89,20 @@ static keywords void PurgeThread(intptr_t h) { } } -static keywords void sys_execve_killer(void) { - struct Dll *e; - pthread_spin_lock(&_pthread_lock); - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { - enum PosixThreadStatus status; - struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); - int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (tid <= 0 || tid == __imp_GetCurrentThreadId()) continue; - status = atomic_load_explicit(&pt->status, memory_order_acquire); - if (status >= kPosixThreadTerminated) continue; - int64_t hand; - if ((hand = __imp_OpenThread(kNtThreadTerminate, false, tid))) { - __imp_TerminateThread(hand, SIGKILL); - __imp_CloseHandle(hand); +static keywords void sys_execve_inherit(int64_t hands[3], bool32 bInherit) { + for (int i = 0; i < 3; ++i) { + if (hands[i] != -1) { + __imp_SetHandleInformation(hands[i], kNtHandleFlagInherit, bInherit); } } - pthread_spin_unlock(&_pthread_lock); } keywords int sys_execve_nt(const char *program, char *const argv[], char *const envp[]) { size_t i; - // validate api usage - if (strlen(program) + 4 < PATH_MAX) { - char progbuf[PATH_MAX]; - char *end = stpcpy(progbuf, program); - char suffixes[][5] = {"", ".com", ".exe"}; - for (i = 0; i < ARRAYLEN(suffixes); ++i) { - stpcpy(end, suffixes[i]); - if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) { - break; - } else if (__imp_GetLastError() == kNtErrorSharingViolation) { - return etxtbsy(); // TODO(jart): does this work - } else { - return eacces(); - } - } - } else { - return enametoolong(); - } - - // - // POINT OF NO RETURN - // - // - // NO! MNO! - // MNO!! [NBK] MNNOO! - // MMNO! MNNOO!! - // MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!! - // !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO! - // ! MMMMMMMMMMMMMPPPPOOOOIII! ! - // MMMMMMMMMMMMPPPPPOOOOOOII!! - // MMMMMOOOOOOPPPPPPPPOOOOMII! - // MMMMM.. OPPMMP .,OMI! - // MMMM:: o.,OPMP,.o ::I!! - // NNM:::.,,OOPM!P,.::::!! - // MMNNNNNOOOOPMO!!IIPPO!!O! - // MMMMMNNNNOO:!!:!!IPPPPOO! - // MMMMMNNOOMMNNIIIPPPOO!! - // MMMONNMMNNNIIIOO! - // MN MOMMMNNNIIIIIO! OO - // MNO! IiiiiiiiiiiiI OOOO - // NNN.MNO! O!!!!!!!!!O OONO NO! - // MNNNNNO! OOOOOOOOOOO MMNNON! - // MNNNNO! PPPPPPPPP MMNON! - // OO! ON! - // - // - - // kill siblings - sys_execve_killer(); - PurgeThread(*_weaken(__sigchld_thread)); - PurgeThread(*_weaken(__sigwinch_thread)); + __hand_lock(); + pthread_spin_lock(&_pthread_lock); // pass bitmask telling child which fds are sockets int bits; @@ -196,7 +125,6 @@ keywords int sys_execve_nt(const char *program, char *const argv[], for (i = 0; i <= 2; ++i) { if (g_fds.p[i].kind != kFdEmpty && // !(g_fds.p[i].flags & O_CLOEXEC)) { - __imp_SetHandleInformation(g_fds.p[i].handle, kNtHandleFlagInherit, true); si.stdiofds[i] = g_fds.p[i].handle; } else { si.stdiofds[i] = -1; @@ -205,24 +133,41 @@ keywords int sys_execve_nt(const char *program, char *const argv[], // launch the process struct NtProcessInformation pi; + sys_execve_inherit(si.stdiofds, true); int rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &si, &pi); if (rc == -1) { - STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program); + sys_execve_inherit(si.stdiofds, false); + __hand_unlock(); if (__imp_GetLastError() == kNtErrorSharingViolation) { - __imp_ExitProcess(SIGVTALRM); // is ETXTBSY + return etxtbsy(); } else { - __imp_ExitProcess(127 << 8); + return -1; } } PurgeHandle(pi.hThread); - // remove duplicate handles - for (i = 0; i <= 2; ++i) { - PurgeHandle(si.stdiofds[i]); + // kill siblings + struct Dll *e; + for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { + struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); + if ((pthread_t)pt == __get_tls()->tib_pthread) continue; + PurgeThread(pt->tib->tib_syshand); + PurgeHandle(pt->semaphore); + } + if (_weaken(__sigwinch_thread)) { + PurgeThread(*_weaken(__sigwinch_thread)); + } + if (_weaken(__itimer)) { + PurgeThread(_weaken(__itimer)->thread); + } + if (_weaken(__proc)) { + PurgeThread(_weaken(__proc)->thread); + PurgeHandle(_weaken(__proc)->onstart); } // retreat to original win32-provided stack memory - __switch_stacks(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack); + __ftrace = 0; + __stack_call(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack); } // child is in same process group so wait for it to get killed by this @@ -249,9 +194,6 @@ static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) { } } } - if (_weaken(__klog_handle)) { - PurgeHandle(*_weaken(__klog_handle)); - } // free all the memory mmap created for (i = 0; i < _mmi.i; ++i) { @@ -268,17 +210,14 @@ static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) { // what those resources would be for process objects, however // this status has actually been observed when waiting on 'em do { - if (__imp_WaitForSingleObject(h, -1) == kNtWaitFailed) { - notpossible; - } - if (!__imp_GetExitCodeProcess(h, &dwExitCode)) { - notpossible; + dwExitCode = 255; + if (__imp_WaitForSingleObject(h, -1) != kNtWaitFailed) { + __imp_GetExitCodeProcess(h, &dwExitCode); } } while (dwExitCode == kNtStillActive); // propagate child exit status to parent - __imp_ExitProcess(dwExitCode); - __builtin_unreachable(); + TerminateThisProcess(dwExitCode); } #endif /* __x86_64__ */ diff --git a/libc/calls/execve-sysv.c b/libc/proc/execve-sysv.c similarity index 92% rename from libc/calls/execve-sysv.c rename to libc/proc/execve-sysv.c index a53dfab96..6120edec8 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/proc/execve-sysv.c @@ -22,7 +22,7 @@ #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" -#include "libc/calls/execve.internal.h" +#include "libc/proc/execve.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/cosmo.h" #include "libc/dce.h" @@ -36,6 +36,7 @@ #include "libc/mem/alloca.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" @@ -114,7 +115,14 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) { // allocate memory int argc; for (argc = 0; argv[argc];) ++argc; - char **shargs = alloca((argc + 4) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (argc + 4) * sizeof(char *); + int ntotal = nbytes + PATH_MAX; + if (__get_safe_size(ntotal, 4096) < ntotal) return enomem(); + char **shargs = alloca(nbytes); + CheckLargeStackAllocation(shargs, nbytes); +#pragma GCC pop_options // try ape loader if (IsApeFile(prog)) { diff --git a/libc/calls/execve.c b/libc/proc/execve.c similarity index 94% rename from libc/calls/execve.c rename to libc/proc/execve.c index 2bd7b2a75..586c71ead 100644 --- a/libc/calls/execve.c +++ b/libc/proc/execve.c @@ -52,6 +52,10 @@ * @param envp[0,n-2] specifies "foo=bar" environment variables * @param envp[n-1] is NULL * @return doesn't return, or -1 w/ errno + * @raise ETXTBSY if another process has `prog` open in write mode + * @raise ENOEXEC if file is executable but not a valid format + * @raise ENOMEM if remaining stack memory is insufficient + * @raise EACCES if execute permission was denied * @asyncsignalsafe * @vforksafe */ diff --git a/libc/calls/execve.internal.h b/libc/proc/execve.internal.h similarity index 100% rename from libc/calls/execve.internal.h rename to libc/proc/execve.internal.h diff --git a/libc/calls/execvp.c b/libc/proc/execvp.c similarity index 100% rename from libc/calls/execvp.c rename to libc/proc/execvp.c diff --git a/libc/calls/execvpe.c b/libc/proc/execvpe.c similarity index 89% rename from libc/calls/execvpe.c rename to libc/proc/execvpe.c index 6d65ea1c9..e2b0a57c9 100644 --- a/libc/calls/execvpe.c +++ b/libc/proc/execvpe.c @@ -22,6 +22,8 @@ #include "libc/limits.h" #include "libc/mem/alloca.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" @@ -63,8 +65,14 @@ int execvpe(const char *prog, char *const argv[], char *const *envp) { // otherwise the program won't have much luck finding itself if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) { for (i = 0; argv[i++];) (void)0; - argv2 = alloca(i * sizeof(*argv)); - memcpy(argv2, argv, i * sizeof(*argv)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = i * sizeof(*argv); + if (__get_safe_size(nbytes, 4096) < nbytes) return enomem(); + argv2 = alloca(nbytes); + CheckLargeStackAllocation(argv2, nbytes); +#pragma GCC pop_options + memcpy(argv2, argv, nbytes); argv2[0] = exe; argv = argv2; } diff --git a/libc/runtime/fork-nt.c b/libc/proc/fork-nt.c similarity index 87% rename from libc/runtime/fork-nt.c rename to libc/proc/fork-nt.c index 4caefa948..4d51c4a8b 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/proc/fork-nt.c @@ -16,33 +16,29 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/internal.h" -#include "libc/calls/ntspawn.h" +#include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" -#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/wincrash.internal.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" #include "libc/intrin/directmap.internal.h" -#include "libc/intrin/handlock.internal.h" +#include "libc/intrin/dll.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" -#include "libc/mem/alloca.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/nt2sysv.h" #include "libc/nt/console.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" -#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" -#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/startf.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" @@ -53,30 +49,34 @@ #include "libc/nt/signals.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/proc/ntspawn.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" -#include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" -#include "libc/sock/internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/limits.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/sysv/errfuns.h" +#include "libc/thread/itimer.internal.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/tls.h" #ifdef __x86_64__ +extern long __klog_handle; extern int64_t __wincrashearly; bool32 __onntconsoleevent(uint32_t); -void sys_setitimer_nt_reset(void); static textwindows wontreturn void AbortFork(const char *func) { #ifdef SYSDEBUG kprintf("fork() %s() failed with win32 error %d\n", func, GetLastError()); #endif - ExitProcess(11); + TerminateThisProcess(SIGSTKFLT); } static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) { @@ -112,7 +112,6 @@ static dontinline textwindows bool ForkIo2(int64_t h, void *buf, size_t n, static dontinline textwindows bool WriteAll(int64_t h, void *buf, size_t n) { bool ok; - // kprintf("WriteAll(%ld, %p, %zu);\n", h, buf, n); ok = ForkIo2(h, buf, n, WriteFile, "WriteFile", false); #ifndef NDEBUG if (ok) ok = ForkIo2(h, &n, sizeof(n), WriteFile, "WriteFile", false); @@ -128,10 +127,10 @@ static dontinline textwindows bool WriteAll(int64_t h, void *buf, size_t n) { } static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) { - // kprintf("ReadOrDie(%ld, %p, %zu);\n", h, buf, n); if (!ForkIo2(h, buf, n, ReadFile, "ReadFile", true)) { AbortFork("ReadFile1"); } + if (_weaken(__klog_handle)) *_weaken(__klog_handle) = 0; #ifndef NDEBUG size_t got; if (!ForkIo2(h, &got, sizeof(got), ReadFile, "ReadFile", true)) { @@ -141,7 +140,6 @@ static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) { AbortFork("ReadFile_SIZE_CHECK"); } #endif - // Sleep(10); } static textwindows int64_t MapOrDie(uint32_t prot, uint64_t size) { @@ -182,13 +180,13 @@ TryAgain: } } -static textwindows int OnForkCrash(struct NtExceptionPointers *ep) { +static __msabi textwindows int OnForkCrash(struct NtExceptionPointers *ep) { kprintf("error: fork() child crashed!%n" "\tExceptionCode = %#x%n" "\tRip = %x%n", ep->ExceptionRecord->ExceptionCode, ep->ContextRecord ? ep->ContextRecord->Rip : -1); - ExitProcess(11); + TerminateThisProcess(SIGSTKFLT); } textwindows void WinMainForked(void) { @@ -213,7 +211,7 @@ textwindows void WinMainForked(void) { NTTRACE("WinMainForked()"); SetEnvironmentVariable(u"_FORK", NULL); #ifdef SYSDEBUG - int64_t oncrash = AddVectoredExceptionHandler(1, NT2SYSV(OnForkCrash)); + int64_t oncrash = AddVectoredExceptionHandler(1, (void *)OnForkCrash); #endif ParseInt(fvar, &reader); @@ -232,7 +230,7 @@ textwindows void WinMainForked(void) { if (IsAsan()) { shad = (char *)(((intptr_t)maps >> 3) + 0x7fff8000); size = ROUNDUP(specialz >> 3, FRAMESIZE); - ViewOrDie(MapOrDie(kNtPageReadwrite, size), kNtFileMapWrite, 0, size, maps); + ViewOrDie(MapOrDie(kNtPageReadwrite, size), kNtFileMapWrite, 0, size, shad); ReadOrDie(reader, shad, (mapcount * sizeof(_mmi.p[0])) >> 3); } @@ -302,14 +300,8 @@ textwindows void WinMainForked(void) { #ifdef SYSDEBUG RemoveVectoredExceptionHandler(oncrash); #endif - if (_weaken(__wincrash)) { - if (!IsTiny()) { - RemoveVectoredExceptionHandler(__wincrashearly); - } - AddVectoredExceptionHandler(1, (void *)_weaken(__wincrash)); - } - if (_weaken(__onntconsoleevent)) { - SetConsoleCtrlHandler(_weaken(__onntconsoleevent), 1); + if (_weaken(__sig_init)) { + _weaken(__sig_init)(); } // jump back into function below @@ -317,6 +309,9 @@ textwindows void WinMainForked(void) { } static void __hand_inherit(bool32 bInherit) { + struct CosmoTib *tib = __get_tls(); + SetHandleInformation(tib->tib_syshand, kNtHandleFlagInherit, bInherit); + SetHandleInformation(tib->tib_syshand, kNtHandleFlagInherit, bInherit); for (int i = 0; i < _mmi.i; ++i) { if ((_mmi.p[i].flags & MAP_TYPE) == MAP_SHARED) { SetHandleInformation(_mmi.p[i].h, kNtHandleFlagInherit, bInherit); @@ -332,26 +327,28 @@ static void __hand_inherit(bool32 bInherit) { } textwindows int sys_fork_nt(uint32_t dwCreationFlags) { + char ok; jmp_buf jb; uint32_t op; char **args; - char ok, threaded; + int i, rc = -1; + struct Proc *proc; struct CosmoTib *tib; char16_t pipename[64]; int64_t reader, writer; struct NtStartupInfo startinfo; - int i, pid, untrackpid, rc = -1; char *p, forkvar[6 + 21 + 1 + 21 + 1]; struct NtProcessInformation procinfo; - threaded = __threaded; - tib = __tls_enabled ? __get_tls() : 0; + tib = __get_tls(); + ftrace_enabled(-1); + strace_enabled(-1); + if (!(proc = __proc_new())) return -1; if (!setjmp(jb)) { - pid = untrackpid = __reservefd_unlocked(-1); reader = CreateNamedPipe(__create_pipe_name(pipename), kNtPipeAccessInbound, kNtPipeTypeByte | kNtPipeReadmodeByte, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable); writer = CreateFile(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, 0, 0); - if (pid != -1 && reader != -1 && writer != -1) { + if (reader != -1 && writer != -1) { p = stpcpy(forkvar, "_FORK="); p = FormatUint64(p, reader); bzero(&startinfo, sizeof(startinfo)); @@ -366,9 +363,13 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { // forked process since the flag was removed by __intercept_flag if (strace_enabled(0) > 0) { int n; - char **args2; for (n = 0; args[n];) ++n; - args2 = alloca((n + 2) * sizeof(char *)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + int nbytes = (n + 2) * sizeof(char *); + char **args2 = alloca(nbytes); + CheckLargeStackAllocation(args2, nbytes); +#pragma GCC pop_options for (i = 0; i < n; ++i) args2[i] = args[i]; args2[i++] = "--strace"; args2[i] = 0; @@ -376,6 +377,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { } #endif __hand_inherit(true); + NTTRACE("STARTING SPAWN"); int spawnrc = ntspawn(GetProgramExecutableName(), args, environ, forkvar, 0, 0, true, dwCreationFlags, 0, &startinfo, &procinfo); @@ -407,14 +409,12 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { writer = -1; } if (ok) { - g_fds.p[pid].kind = kFdProcess; - g_fds.p[pid].handle = procinfo.hProcess; - g_fds.p[pid].flags = O_CLOEXEC; - g_fds.p[pid].zombie = false; - untrackpid = -1; - rc = pid; + proc->wasforked = true; + proc->handle = procinfo.hProcess; + rc = proc->pid = procinfo.dwProcessId; + __proc_add(proc); } else { - TerminateProcess(procinfo.hProcess, 9); + TerminateProcess(procinfo.hProcess, SIGKILL); CloseHandle(procinfo.hProcess); } } @@ -427,27 +427,28 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { } else { rc = 0; // re-apply code morphing for thread-local storage - if (tib && _weaken(__set_tls) && _weaken(__morph_tls)) { - _weaken(__set_tls)(tib); - _weaken(__morph_tls)(); - __tls_enabled_set(true); - } - // re-apply code morphing for synchronization nops - if (threaded && !__threaded && _weaken(__enable_threads)) { - _weaken(__enable_threads)(); - } + __set_tls(tib); + __morph_tls(); + __tls_enabled_set(true); + // clear pending signals + tib->tib_sigpending = 0; + __sig.pending = 0; + // re-enable threads + __enable_threads(); // re-apply code morphing for function tracing if (ftrace_stackdigs) { _weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)()); } // reset alarms - if (_weaken(sys_setitimer_nt_reset)) { - _weaken(sys_setitimer_nt_reset)(); + if (_weaken(__itimer_reset)) { + _weaken(__itimer_reset)(); } } - if (untrackpid != -1) { - __releasefd(untrackpid); + if (rc == -1) { + __proc_free(proc); } + ftrace_enabled(+1); + strace_enabled(+1); return rc; } diff --git a/libc/runtime/fork-sysv.c b/libc/proc/fork-sysv.c similarity index 98% rename from libc/runtime/fork-sysv.c rename to libc/proc/fork-sysv.c index fca116d01..0a26edd74 100644 --- a/libc/runtime/fork-sysv.c +++ b/libc/proc/fork-sysv.c @@ -58,7 +58,7 @@ int sys_fork(void) { : "x8", "x16", "memory"); return _sysret(res_x0); } else if (__syslib) { - return _sysret(__syslib->fork()); + return _sysret(__syslib->__fork()); } else { return enosys(); } diff --git a/libc/runtime/fork.c b/libc/proc/fork.c similarity index 74% rename from libc/runtime/fork.c rename to libc/proc/fork.c index 127b786da..8ff302ba9 100644 --- a/libc/runtime/fork.c +++ b/libc/proc/fork.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" @@ -25,20 +26,28 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/nt/process.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/sig.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/tls.h" int _fork(uint32_t dwCreationFlags) { + struct Dll *e; struct CosmoTib *tib; - struct PosixThread *pt; int ax, dx, tid, parent; + struct PosixThread *me, *other; (void)parent; BLOCK_SIGNALS; + BLOCK_CANCELLATIONS; + if (IsWindows()) __proc_lock(); if (__threaded && _weaken(_pthread_onfork_prepare)) { _weaken(_pthread_onfork_prepare)(); } @@ -55,24 +64,34 @@ int _fork(uint32_t dwCreationFlags) { } parent = __pid; __pid = dx; - if (__tls_enabled) { - tib = __get_tls(); - tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid(); - atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); - if ((pt = (struct PosixThread *)tib->tib_pthread)) { - atomic_store_explicit(&pt->ptid, tid, memory_order_relaxed); - } + tib = __get_tls(); + me = (struct PosixThread *)tib->tib_pthread; + dll_remove(&_pthread_list, &me->list); + for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { + other = POSIXTHREAD_CONTAINER(e); + atomic_store_explicit(&other->status, kPosixThreadZombie, + memory_order_relaxed); + other->tib->tib_syshand = 0; } + dll_make_first(&_pthread_list, &me->list); + tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid(); + atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); + atomic_store_explicit(&me->ptid, tid, memory_order_relaxed); + atomic_store_explicit(&me->cancelled, false, memory_order_relaxed); + if (IsWindows()) npassert((me->semaphore = CreateSemaphore(0, 0, 1, 0))); if (__threaded && _weaken(_pthread_onfork_child)) { _weaken(_pthread_onfork_child)(); } + if (IsWindows()) __proc_wipe(); STRACE("fork() → 0 (child of %d)", parent); } else { if (__threaded && _weaken(_pthread_onfork_parent)) { _weaken(_pthread_onfork_parent)(); } + if (IsWindows()) __proc_unlock(); STRACE("fork() → %d% m", ax); } + ALLOW_CANCELLATIONS; ALLOW_SIGNALS; return ax; } diff --git a/libc/proc/kill-nt.c b/libc/proc/kill-nt.c new file mode 100644 index 000000000..df9d73e84 --- /dev/null +++ b/libc/proc/kill-nt.c @@ -0,0 +1,76 @@ +/*-*- 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 │ +│ │ +│ 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/intrin/dll.h" +#include "libc/nt/errors.h" +#include "libc/nt/runtime.h" +#include "libc/proc/proc.internal.h" +#include "libc/sysv/errfuns.h" +#ifdef __x86_64__ + +static textwindows int sys_kill_nt_impl(int pid, int sig) { + int err; + bool32 ok; + struct Dll *e; + struct Proc *pr = 0; + for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) { + if (pid == PROC_CONTAINER(e)->pid) { + pr = PROC_CONTAINER(e); + } + } + if (!pr) { + return esrch(); + } + if (sig) { + err = errno; + ok = TerminateProcess(pr->handle, sig); + if (!ok && GetLastError() == kNtErrorAccessDenied) { + ok = true; // cargo culting other codebases here + errno = err; + } + } + return ok ? 0 : -1; +} + +textwindows int sys_kill_nt(int pid, int sig) { + int rc; + if (!(0 <= sig && sig <= 64)) return einval(); + + // XXX: NT doesn't really have process groups. For instance the + // CreateProcess() flag for starting a process group actually + // just does an "ignore ctrl-c" internally. + if (pid < -1) pid = -pid; + + if (pid == -1) return einval(); // no support for kill all yet + + // If we're targeting current process group then just call raise(). + if (pid <= 0 || pid == getpid()) { + if (!sig) return 0; // ability check passes + return raise(sig); + } + + __proc_lock(); + rc = sys_kill_nt_impl(pid, sig); + __proc_unlock(); + + return rc; +} + +#endif /* __x86_64__ */ diff --git a/libc/calls/kill.c b/libc/proc/kill.c similarity index 100% rename from libc/calls/kill.c rename to libc/proc/kill.c diff --git a/libc/calls/killpg.c b/libc/proc/killpg.c similarity index 100% rename from libc/calls/killpg.c rename to libc/proc/killpg.c diff --git a/libc/calls/ntspawn.c b/libc/proc/ntspawn.c similarity index 82% rename from libc/calls/ntspawn.c rename to libc/proc/ntspawn.c index 2c41b44f2..db09594b6 100644 --- a/libc/calls/ntspawn.c +++ b/libc/proc/ntspawn.c @@ -16,11 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/assert.h" #include "libc/calls/struct/sigaction.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/pushpop.internal.h" #include "libc/macros.internal.h" #include "libc/nt/enum/filemapflags.h" @@ -33,6 +34,7 @@ #include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/securityattributes.h" #include "libc/nt/struct/startupinfo.h" +#include "libc/str/str.h" #include "libc/sysv/errfuns.h" #ifdef __x86_64__ @@ -79,27 +81,42 @@ textwindows int ntspawn( const char16_t *opt_lpCurrentDirectory, const struct NtStartupInfo *lpStartupInfo, struct NtProcessInformation *opt_out_lpProcessInformation) { - int rc; int64_t handle; - struct SpawnBlock *block; - char16_t prog16[PATH_MAX]; - rc = -1; - block = NULL; - _init_sigchld(); + int i, e, rc = -1; + struct SpawnBlock *block = 0; + char16_t prog16[PATH_MAX + 5], *p; + char16_t suffixes[][5] = {u"", u".com", u".exe"}; + if (__mkntpath(prog, prog16) == -1) return -1; if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0, sizeof(*block), 0)) && (block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, sizeof(*block), 0)) && mkntcmdline(block->cmdline, argv) != -1 && - mkntenvblock(block->envvars, envp, extravar, block->buf) != -1 && - CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, - opt_lpThreadAttributes, bInheritHandles, - dwCreationFlags | kNtCreateUnicodeEnvironment | - kNtInheritParentAffinity, - block->envvars, opt_lpCurrentDirectory, lpStartupInfo, - opt_out_lpProcessInformation)) { - rc = 0; + mkntenvblock(block->envvars, envp, extravar, block->buf) != -1) { + p = prog16 + strlen16(prog16); + for (i = 0; i < ARRAYLEN(suffixes); ++i) { + if (suffixes[i][0] && endswith16(prog16, suffixes[i])) { + p -= strlen16(suffixes[i]); + *p = 0; + } else { + strcpy16(p, suffixes[i]); + } + e = errno; + if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, + opt_lpThreadAttributes, bInheritHandles, + dwCreationFlags | kNtCreateUnicodeEnvironment | + kNtInheritParentAffinity, + block->envvars, opt_lpCurrentDirectory, lpStartupInfo, + opt_out_lpProcessInformation)) { + rc = 0; + break; + } else if (errno == ENOENT) { + errno = e; + } else { + break; + } + } } else if (GetLastError() == kNtErrorSharingViolation) { etxtbsy(); } diff --git a/libc/calls/ntspawn.h b/libc/proc/ntspawn.h similarity index 100% rename from libc/calls/ntspawn.h rename to libc/proc/ntspawn.h diff --git a/libc/stdio/paginate.c b/libc/proc/paginate.c similarity index 87% rename from libc/stdio/paginate.c rename to libc/proc/paginate.c index 5cc4483b0..87687163c 100644 --- a/libc/stdio/paginate.c +++ b/libc/proc/paginate.c @@ -17,14 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/intrin/safemacros.internal.h" #include "libc/limits.h" -#include "libc/stdio/stdio.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" -#include "libc/sysv/consts/o.h" -#include "libc/x/x.h" +#include "libc/temp.h" /** * Displays wall of text in terminal with pagination. @@ -32,15 +29,12 @@ void __paginate(int fd, const char *s) { int tfd, pid; char *args[3] = {0}; - char tmppath[PATH_MAX]; + char tmppath[] = "/tmp/paginate.XXXXXX"; char progpath[PATH_MAX]; if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && ((args[0] = commandv("less", progpath, sizeof(progpath))) || (args[0] = commandv("more", progpath, sizeof(progpath))))) { - snprintf(tmppath, sizeof(tmppath), "%s%s-%s-%d.txt", kTmpPath, - firstnonnull(program_invocation_short_name, "unknown"), "paginate", - getpid()); - if ((tfd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0644)) != -1) { + if ((tfd = mkstemp(tmppath)) != -1) { write(tfd, s, strlen(s)); close(tfd); args[1] = tmppath; diff --git a/libc/stdio/posix_spawn.c b/libc/proc/posix_spawn.c similarity index 91% rename from libc/stdio/posix_spawn.c rename to libc/proc/posix_spawn.c index a2a3cb0a7..7d32b1e14 100644 --- a/libc/stdio/posix_spawn.c +++ b/libc/proc/posix_spawn.c @@ -16,12 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #include "libc/assert.h" #include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/rlimit.h" @@ -51,10 +51,11 @@ #include "libc/nt/runtime.h" #include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/startupinfo.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" -#include "libc/stdio/posix_spawn.h" -#include "libc/stdio/posix_spawn.internal.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" @@ -85,7 +86,7 @@ #define sigprocmask sys_sigprocmask #endif -static atomic_bool real_vfork; // i.e. not qemu/wsl/xnu/openbsd +static atomic_bool has_vfork; // i.e. not qemu/wsl/xnu/openbsd static void posix_spawn_unhand(int64_t hands[3]) { for (int i = 0; i < 3; ++i) { @@ -118,8 +119,12 @@ static textwindows errno_t posix_spawn_windows_impl( } } - // reserve a fake pid for this spawn - int child = __reservefd(-1); + // reserve object for tracking proces + struct Proc *proc; + __proc_lock(); + proc = __proc_new(); + __proc_unlock(); + if (!proc) return -1; // apply user file actions intptr_t close_handle[3] = {-1, -1, -1}; @@ -166,7 +171,9 @@ static textwindows errno_t posix_spawn_windows_impl( } if (err) { posix_spawn_unhand(close_handle); - __releasefd(child); + __proc_lock(); + __proc_free(proc); + __proc_unlock(); return err; } } @@ -221,20 +228,21 @@ static textwindows errno_t posix_spawn_windows_impl( __hand_runlock(); if (rc == -1) { int err = errno; - __releasefd(child); + __proc_lock(); + __proc_free(proc); + __proc_unlock(); errno = e; return err; } - // track the process - CloseHandle(procinfo.hThread); - g_fds.p[child].kind = kFdProcess; - g_fds.p[child].handle = procinfo.hProcess; - g_fds.p[child].flags = O_CLOEXEC; - g_fds.p[child].zombie = false; - // return the result - if (pid) *pid = child; + CloseHandle(procinfo.hThread); + proc->pid = procinfo.dwProcessId; + proc->handle = procinfo.hProcess; + if (pid) *pid = proc->pid; + __proc_lock(); + __proc_add(proc); + __proc_unlock(); return 0; } @@ -267,6 +275,7 @@ static textwindows dontinline errno_t posix_spawn_windows( * Spawns process, the POSIX way. * * This provides superior process creation performance across systems + * * Processes are normally spawned by calling fork() and execve(), but * that goes slow on Windows if the caller has allocated a nontrivial * number of memory mappings, all of which need to be copied into the @@ -278,6 +287,10 @@ static textwindows dontinline errno_t posix_spawn_windows( * benefit of avoiding the footguns of using vfork() directly because * this implementation will ensure signal handlers can't be called in * the child process since that'd likely corrupt the parent's memory. + * On systems with a real vfork() implementation, the execve() status + * code is returned by this function via shared memory; otherwise, it + * gets passed via a temporary pipe (on systems like QEmu, Blink, and + * XNU/OpenBSD) whose support is auto-detected at runtime. * * @param pid if non-null shall be set to child pid on success * @param path is resolved path of program which is not `$PATH` searched @@ -285,6 +298,10 @@ static textwindows dontinline errno_t posix_spawn_windows( * @param attrp specifies signal masks, user ids, scheduling, etc. * @param envp is environment variables, or `environ` if null * @return 0 on success or error number on failure + * @raise ETXTBSY if another process has `path` open in write mode + * @raise ENOEXEC if file is executable but not a valid format + * @raise ENOMEM if remaining stack memory is insufficient + * @raise EACCES if execute permission was denied * @see posix_spawnp() for `$PATH` searching * @returnserrno * @tlsrequired @@ -306,7 +323,7 @@ errno_t posix_spawn(int *pid, const char *path, sigfillset(&blockall); sigprocmask(SIG_SETMASK, &blockall, &oldmask); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); - if ((use_pipe = !atomic_load_explicit(&real_vfork, memory_order_acquire))) { + if ((use_pipe = !atomic_load_explicit(&has_vfork, memory_order_acquire))) { if (pipe2(pfds, O_CLOEXEC)) { res = errno; goto ParentFailed; @@ -433,7 +450,7 @@ errno_t posix_spawn(int *pid, const char *path, res = status; } else { if (can_clobber) { - atomic_store_explicit(&real_vfork, true, memory_order_release); + atomic_store_explicit(&has_vfork, true, memory_order_release); } res = 0; read(pfds[0], &res, sizeof(res)); diff --git a/libc/stdio/posix_spawn.h b/libc/proc/posix_spawn.h similarity index 100% rename from libc/stdio/posix_spawn.h rename to libc/proc/posix_spawn.h diff --git a/libc/stdio/posix_spawn.internal.h b/libc/proc/posix_spawn.internal.h similarity index 85% rename from libc/stdio/posix_spawn.internal.h rename to libc/proc/posix_spawn.internal.h index 1a80b95cb..5d9cfefed 100644 --- a/libc/stdio/posix_spawn.internal.h +++ b/libc/proc/posix_spawn.internal.h @@ -3,6 +3,7 @@ #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigset.h" +#include "libc/proc/posix_spawn.h" #define _POSIX_SPAWN_CLOSE 1 #define _POSIX_SPAWN_DUP2 2 @@ -36,6 +37,9 @@ struct _posix_faction { char *path; }; +int __posix_spawn_add_file_action(posix_spawn_file_actions_t *, + struct _posix_faction); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_ */ diff --git a/libc/proc/posix_spawn_add_file_action.c b/libc/proc/posix_spawn_add_file_action.c new file mode 100644 index 000000000..854116f76 --- /dev/null +++ b/libc/proc/posix_spawn_add_file_action.c @@ -0,0 +1,32 @@ +/*-*- 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/errno.h" +#include "libc/mem/mem.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +int __posix_spawn_add_file_action(posix_spawn_file_actions_t *l, + struct _posix_faction a) { + struct _posix_faction *ap; + if (!(ap = malloc(sizeof(*ap)))) return ENOMEM; + *ap = a; + while (*l) l = &(*l)->next; + *l = ap; + return 0; +} diff --git a/libc/proc/posix_spawn_file_actions_addclose.c b/libc/proc/posix_spawn_file_actions_addclose.c new file mode 100644 index 000000000..3bf4a7ee1 --- /dev/null +++ b/libc/proc/posix_spawn_file_actions_addclose.c @@ -0,0 +1,41 @@ +/*-*- 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/dce.h" +#include "libc/errno.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Add a close action to object. + * + * @param file_actions was initialized by posix_spawn_file_actions_init() + * @return 0 on success, or errno on error + * @raise ENOMEM if we require more vespene gas + * @raise EBADF if `fildes` is negative + */ +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, + int fildes) { + if (fildes < 0) return EBADF; + if (IsWindows() && fildes > 2) return 0; + return __posix_spawn_add_file_action(file_actions, + (struct _posix_faction){ + .action = _POSIX_SPAWN_CLOSE, + .fildes = fildes, + }); +} diff --git a/test/libc/calls/tkill_test.c b/libc/proc/posix_spawn_file_actions_adddup2.c similarity index 62% rename from test/libc/calls/tkill_test.c rename to libc/proc/posix_spawn_file_actions_adddup2.c index 44b1583f1..449a86b2c 100644 --- a/test/libc/calls/tkill_test.c +++ b/libc/proc/posix_spawn_file_actions_adddup2.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,44 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/sysv/consts/sig.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" -_Thread_local intptr_t gotsig; - -void OnSig(int sig) { - gotsig = sig; -} - -void *Worker(void *arg) { - sigset_t ss; - sigemptyset(&ss); - ASSERT_SYS(EINTR, -1, sigsuspend(&ss)); - return (void *)gotsig; -} - -TEST(tkill, test) { - if (IsWindows()) return; // TODO(jart): fix me - int tid; - void *res; - pthread_t t; - sigset_t ss, oldss; - sighandler_t oldsig; - sigemptyset(&ss); - sigaddset(&ss, SIGUSR1); - oldsig = signal(SIGUSR1, OnSig); - ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss)); - ASSERT_EQ(0, pthread_create(&t, 0, Worker, 0)); - ASSERT_EQ(0, pthread_getunique_np(t, &tid)); - ASSERT_SYS(0, 0, tkill(tid, SIGUSR1)); - ASSERT_EQ(0, pthread_join(t, &res)); - ASSERT_EQ(SIGUSR1, (intptr_t)res); - ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0)); - signal(SIGUSR1, oldsig); +/** + * Add a dup2 action to object. + * + * @param file_actions was initialized by posix_spawn_file_actions_init() + * @return 0 on success, or errno on error + * @raise ENOMEM if we require more vespene gas + * @raise EBADF if 'fildes' or `newfildes` is negative + * @raise ENOTSUP if `newfildes` isn't 0, 1, or 2 on Windows + */ +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, + int fildes, int newfildes) { + if (fildes < 0 || newfildes < 0) return EBADF; + if (IsWindows() && newfildes > 2) return ENOTSUP; + return __posix_spawn_add_file_action(file_actions, + (struct _posix_faction){ + .action = _POSIX_SPAWN_DUP2, + .fildes = fildes, + .newfildes = newfildes, + }); } diff --git a/libc/calls/samplepids.c b/libc/proc/posix_spawn_file_actions_addopen.c similarity index 56% rename from libc/calls/samplepids.c rename to libc/proc/posix_spawn_file_actions_addopen.c index ae0f144ad..3f369489c 100644 --- a/libc/calls/samplepids.c +++ b/libc/proc/posix_spawn_file_actions_addopen.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,36 +16,36 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/dce.h" -#include "libc/stdio/rand.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" /** - * Returns handles of windows pids being tracked. + * Add an open action to object. * - * We return 64 at most because Windows can't await on a larger number - * of things at the same time. If we have a lot of subprocesses, then we - * choose a subgroup to monitor at random. - * - * @return number of items returned in pids and handles + * @param file_actions was initialized by posix_spawn_file_actions_init() + * @param fildes is what open() result gets duplicated to + * @param path will be safely copied + * @return 0 on success, or errno on error + * @raise ENOMEM if we require more vespene gas + * @raise EBADF if `fildes` is negative + * @raise ENOTSUP if `fildes` isn't 0, 1, or 2 on Windows */ -textwindows int __sample_pids(int pids[hasatleast 64], - int64_t handles[hasatleast 64], - bool exploratory) { - uint32_t i, j, base, count; - base = _rand64() >> 32; - for (count = i = 0; i < g_fds.n; ++i) { - j = (base + i) % g_fds.n; - if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) { - pids[count] = j; - handles[count] = g_fds.p[j].handle; - if (++count == 64) { - break; - } - } - } - return count; +int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions, + int fildes, const char *path, int oflag, + unsigned mode) { + char *path2; + if (fildes < 0) return EBADF; + if (IsWindows() && fildes > 2) return ENOTSUP; + if (!(path2 = strdup(path))) return ENOMEM; + return __posix_spawn_add_file_action(file_actions, + (struct _posix_faction){ + .action = _POSIX_SPAWN_OPEN, + .fildes = fildes, + .path = path2, + .oflag = oflag, + .mode = mode, + }); } diff --git a/libc/proc/posix_spawn_file_actions_destroy.c b/libc/proc/posix_spawn_file_actions_destroy.c new file mode 100644 index 000000000..9e769327c --- /dev/null +++ b/libc/proc/posix_spawn_file_actions_destroy.c @@ -0,0 +1,39 @@ +/*-*- 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/mem/mem.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Destroys posix_spawn() file actions list. + * + * This function is safe to call multiple times. + * + * @param file_actions was initialized by posix_spawn_file_actions_init() + * @return 0 on success, or errno on error + */ +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) { + if (*file_actions) { + posix_spawn_file_actions_destroy(&(*file_actions)->next); + free((*file_actions)->path); + free(*file_actions); + *file_actions = 0; + } + return 0; +} diff --git a/libc/proc/posix_spawn_file_actions_init.c b/libc/proc/posix_spawn_file_actions_init.c new file mode 100644 index 000000000..a7a118ca6 --- /dev/null +++ b/libc/proc/posix_spawn_file_actions_init.c @@ -0,0 +1,33 @@ +/*-*- 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/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Initializes posix_spawn() file actions list. + * + * File actions get applied in the same order as they're registered. + * + * @param file_actions will need posix_spawn_file_actions_destroy() + * @return 0 on success, or errno on error + */ +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) { + *file_actions = 0; + return 0; +} diff --git a/libc/proc/posix_spawnattr_destroy.c b/libc/proc/posix_spawnattr_destroy.c new file mode 100644 index 000000000..b2d1f22b4 --- /dev/null +++ b/libc/proc/posix_spawnattr_destroy.c @@ -0,0 +1,36 @@ +/*-*- 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/mem/mem.h" +#include "libc/proc/posix_spawn.h" + +/** + * Destroys posix_spawn() attributes object. + * + * This function is safe to call multiple times. + * + * @param attr was initialized by posix_spawnattr_init() + * @return 0 on success, or errno on error + */ +int posix_spawnattr_destroy(posix_spawnattr_t *attr) { + if (*attr) { + free(*attr); + *attr = 0; + } + return 0; +} diff --git a/libc/calls/tramp.c b/libc/proc/posix_spawnattr_getflags.c similarity index 83% rename from libc/calls/tramp.c rename to libc/proc/posix_spawnattr_getflags.c index 56736dfac..48c981718 100644 --- a/libc/calls/tramp.c +++ b/libc/proc/posix_spawnattr_getflags.c @@ -16,17 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/sig.internal.h" -#include "libc/calls/struct/ucontext.internal.h" -#include "libc/calls/ucontext.h" -#ifdef __x86_64__ +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" -textwindows int __sig_tramp(struct Delivery *pkg) { - ucontext_t ctx = {0}; - _ntcontext2linux(&ctx, pkg->nc); - __sig_handle(pkg->ops, pkg->sig, pkg->sic, &ctx); - _ntlinux2context(pkg->nc, &ctx); +/** + * Gets posix_spawn() flags. + * + * @param attr was initialized by posix_spawnattr_init() + * @return 0 on success, or errno on error + */ +int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) { + *flags = (*attr)->flags; return 0; } - -#endif /* __x86_64__ */ diff --git a/libc/proc/posix_spawnattr_getpgroup.c b/libc/proc/posix_spawnattr_getpgroup.c new file mode 100644 index 000000000..464631704 --- /dev/null +++ b/libc/proc/posix_spawnattr_getpgroup.c @@ -0,0 +1,32 @@ +/*-*- 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/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Gets process group id associated with attributes. + * + * @param attr was initialized by posix_spawnattr_init() + * @param pgroup receives the result on success + * @return 0 on success, or errno on error + */ +int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) { + *pgroup = (*attr)->pgroup; + return 0; +} diff --git a/libc/proc/posix_spawnattr_getrlimit.c b/libc/proc/posix_spawnattr_getrlimit.c new file mode 100644 index 000000000..2109bf89c --- /dev/null +++ b/libc/proc/posix_spawnattr_getrlimit.c @@ -0,0 +1,44 @@ +/*-*- 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/struct/rlimit.h" +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Gets resource limit for spawned process. + * + * @return 0 on success, or errno on error + * @raise EINVAL if `resource` is invalid or unsupported by host + * @raise ENOENT if `resource` is absent + */ +int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource, + struct rlimit *rlim) { + if ((0 <= resource && resource < ARRAYLEN((*attr)->rlim))) { + if (((*attr)->rlimset & (1u << resource))) { + *rlim = (*attr)->rlim[resource]; + return 0; + } else { + return ENOENT; + } + } else { + return EINVAL; + } +} diff --git a/test/libc/calls/printargs_test.c b/libc/proc/posix_spawnattr_getschedparam.c similarity index 76% rename from test/libc/calls/printargs_test.c rename to libc/proc/posix_spawnattr_getschedparam.c index d2d3029da..ff36c0798 100644 --- a/test/libc/calls/printargs_test.c +++ b/libc/proc/posix_spawnattr_getschedparam.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,23 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/runtime/runtime.h" -#include "libc/testlib/testlib.h" -#include "libc/x/x.h" - -void SetUpOnce(void) { - ASSERT_SYS(0, 0, pledge("stdio rpath tty proc", 0)); -} +#include "libc/calls/struct/sched_param.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" /** - * @fileoverview platform arguments tool + * Gets scheduler parameter. * - * This is intended to integrate with the Emacs keystroke `C-c C-s` - * which will remotely launch it on all the other operating systems and - * print the output. This way we can audit stuff. + * @param attr was initialized by posix_spawnattr_init() + * @param schedparam receives the result + * @return 0 on success, or errno on error */ - -TEST(printargs, test) { - __printargs("%r"); +int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr, + struct sched_param *schedparam) { + *schedparam = (*attr)->schedparam; + return 0; } diff --git a/libc/proc/posix_spawnattr_getschedpolicy.c b/libc/proc/posix_spawnattr_getschedpolicy.c new file mode 100644 index 000000000..3b1a78e07 --- /dev/null +++ b/libc/proc/posix_spawnattr_getschedpolicy.c @@ -0,0 +1,33 @@ +/*-*- 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/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Gets scheduler policy that'll be used for spawned process. + * + * @param attr was initialized by posix_spawnattr_init() + * @param schedpolicy receives the result + * @return 0 on success, or errno on error + */ +int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, + int *schedpolicy) { + *schedpolicy = (*attr)->schedpolicy; + return 0; +} diff --git a/libc/proc/posix_spawnattr_getsigdefault.c b/libc/proc/posix_spawnattr_getsigdefault.c new file mode 100644 index 000000000..4e208fd8b --- /dev/null +++ b/libc/proc/posix_spawnattr_getsigdefault.c @@ -0,0 +1,32 @@ +/*-*- 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/struct/sigset.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Retrieves which signals will be restored to `SIG_DFL`. + * + * @return 0 on success, or errno on error + */ +int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr, + sigset_t *sigdefault) { + *sigdefault = (*attr)->sigdefault; + return 0; +} diff --git a/libc/proc/posix_spawnattr_getsigmask.c b/libc/proc/posix_spawnattr_getsigmask.c new file mode 100644 index 000000000..a2bbd0f76 --- /dev/null +++ b/libc/proc/posix_spawnattr_getsigmask.c @@ -0,0 +1,36 @@ +/*-*- 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/struct/sigset.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Gets signal mask for sigprocmask() in child process. + * + * The signal mask is applied to the child process in such a way that + * signal handlers from the parent process can't get triggered in the + * child process. + * + * @return 0 on success, or errno on error + */ +int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, + sigset_t *sigmask) { + *sigmask = (*attr)->sigmask; + return 0; +} diff --git a/libc/proc/posix_spawnattr_init.c b/libc/proc/posix_spawnattr_init.c new file mode 100644 index 000000000..6029d577a --- /dev/null +++ b/libc/proc/posix_spawnattr_init.c @@ -0,0 +1,42 @@ +/*-*- 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/errno.h" +#include "libc/mem/mem.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Initialize posix_spawn() attributes object with default values. + * + * @param attr needs to be passed to posix_spawnattr_destroy() later + * @return 0 on success, or errno on error + * @raise ENOMEM if we require more vespene gas + */ +int posix_spawnattr_init(posix_spawnattr_t *attr) { + int rc, e = errno; + struct _posix_spawna *a; + if ((a = calloc(1, sizeof(struct _posix_spawna)))) { + *attr = a; + rc = 0; + } else { + rc = errno; + errno = e; + } + return rc; +} diff --git a/libc/proc/posix_spawnattr_setflags.c b/libc/proc/posix_spawnattr_setflags.c new file mode 100644 index 000000000..12c00e325 --- /dev/null +++ b/libc/proc/posix_spawnattr_setflags.c @@ -0,0 +1,47 @@ +/*-*- 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/errno.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Sets posix_spawn() flags. + * + * @param attr was initialized by posix_spawnattr_init() + * @param flags may have any of the following + * - `POSIX_SPAWN_RESETIDS` + * - `POSIX_SPAWN_SETPGROUP` + * - `POSIX_SPAWN_SETSIGDEF` + * - `POSIX_SPAWN_SETSIGMASK` + * - `POSIX_SPAWN_SETSCHEDPARAM` + * - `POSIX_SPAWN_SETSCHEDULER` + * - `POSIX_SPAWN_SETSID` + * @return 0 on success, or errno on error + * @raise EINVAL if `flags` has invalid bits + */ +int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { + if (flags & + ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | + POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM | + POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSID)) { + return EINVAL; + } + (*attr)->flags = flags; + return 0; +} diff --git a/libc/proc/posix_spawnattr_setpgroup.c b/libc/proc/posix_spawnattr_setpgroup.c new file mode 100644 index 000000000..2d6263d61 --- /dev/null +++ b/libc/proc/posix_spawnattr_setpgroup.c @@ -0,0 +1,38 @@ +/*-*- 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/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Specifies process group into which child process is placed. + * + * Setting `pgroup` to zero will ensure newly created processes are + * placed within their own brand new process group. + * + * This setter also sets the `POSIX_SPAWN_SETPGROUP` flag. + * + * @param attr was initialized by posix_spawnattr_init() + * @param pgroup is the process group id, or 0 for self + * @return 0 on success, or errno on error + */ +int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) { + (*attr)->flags |= POSIX_SPAWN_SETPGROUP; + (*attr)->pgroup = pgroup; + return 0; +} diff --git a/libc/proc/posix_spawnattr_setrlimit.c b/libc/proc/posix_spawnattr_setrlimit.c new file mode 100644 index 000000000..10dabee61 --- /dev/null +++ b/libc/proc/posix_spawnattr_setrlimit.c @@ -0,0 +1,43 @@ +/*-*- 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/struct/rlimit.h" +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Sets resource limit on spawned process. + * + * Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`. + * + * @return 0 on success, or errno on error + * @raise EINVAL if resource is invalid + */ +int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource, + const struct rlimit *rlim) { + if (0 <= resource && resource < ARRAYLEN((*attr)->rlim)) { + (*attr)->flags |= POSIX_SPAWN_SETRLIMIT; + (*attr)->rlimset |= 1u << resource; + (*attr)->rlim[resource] = *rlim; + return 0; + } else { + return EINVAL; + } +} diff --git a/libc/proc/posix_spawnattr_setschedparam.c b/libc/proc/posix_spawnattr_setschedparam.c new file mode 100644 index 000000000..e73fdce01 --- /dev/null +++ b/libc/proc/posix_spawnattr_setschedparam.c @@ -0,0 +1,37 @@ +/*-*- 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/struct/sched_param.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Specifies scheduler parameter override for spawned process. + * + * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`. + * + * @param attr was initialized by posix_spawnattr_init() + * @param schedparam receives the result + * @return 0 on success, or errno on error + */ +int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, + const struct sched_param *schedparam) { + (*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM; + (*attr)->schedparam = *schedparam; + return 0; +} diff --git a/libc/time/dsleep.c b/libc/proc/posix_spawnattr_setschedpolicy.c similarity index 76% rename from libc/time/dsleep.c rename to libc/proc/posix_spawnattr_setschedpolicy.c index ca6c5ffe7..dd1ed863c 100644 --- a/libc/time/dsleep.c +++ b/libc/proc/posix_spawnattr_setschedpolicy.c @@ -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,27 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/timespec.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/time/time.h" - -// todo(jart): delete +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" /** - * Sleeps w/ higher precision. + * Specifies scheduler policy override for spawned process. + * + * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`. + * + * @param attr was initialized by posix_spawnattr_init() + * @return 0 on success, or errno on error */ -long double dsleep(long double secs) { - struct timespec dur, rem; - dur.tv_sec = secs; - dur.tv_nsec = secs * 1e9; - dur.tv_nsec = dur.tv_nsec % 1000000000; - if (secs > 1e-6) { - nanosleep(&dur, &rem); - secs = rem.tv_nsec; - secs *= 1 / 1e9; - secs += rem.tv_sec; - return secs; - } else { - return 0; - } +int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) { + (*attr)->flags |= POSIX_SPAWN_SETSCHEDULER; + (*attr)->schedpolicy = schedpolicy; + return 0; } diff --git a/libc/proc/posix_spawnattr_setsigdefault.c b/libc/proc/posix_spawnattr_setsigdefault.c new file mode 100644 index 000000000..7f277d839 --- /dev/null +++ b/libc/proc/posix_spawnattr_setsigdefault.c @@ -0,0 +1,44 @@ +/*-*- 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/struct/sigset.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Specifies which signals should be restored to `SIG_DFL`. + * + * This routine isn't necessary in most cases, since posix_spawn() by + * default will try to avoid vfork() race conditions by tracking what + * signals have a handler function and then resets them automatically + * within the child process, before applying the child's signal mask. + * This function may be used to ensure the `SIG_IGN` disposition will + * not propagate across execve in cases where this process explicitly + * set the signals to `SIG_IGN` earlier (since posix_spawn() will not + * issue O(128) system calls just to be totally pedantic about that). + * + * Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`. + * + * @return 0 on success, or errno on error + */ +int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, + const sigset_t *sigdefault) { + (*attr)->flags |= POSIX_SPAWN_SETSIGDEF; + (*attr)->sigdefault = *sigdefault; + return 0; +} diff --git a/libc/proc/posix_spawnattr_setsigmask.c b/libc/proc/posix_spawnattr_setsigmask.c new file mode 100644 index 000000000..842fcd8f1 --- /dev/null +++ b/libc/proc/posix_spawnattr_setsigmask.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/sigset.h" +#include "libc/proc/posix_spawn.h" +#include "libc/proc/posix_spawn.internal.h" + +/** + * Specifies signal mask for sigprocmask() in child process. + * + * This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag. + * + * @return 0 on success, or errno on error + */ +int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, + const sigset_t *sigmask) { + (*attr)->flags |= POSIX_SPAWN_SETSIGMASK; + (*attr)->sigmask = *sigmask; + return 0; +} diff --git a/libc/stdio/posix_spawnp.c b/libc/proc/posix_spawnp.c similarity index 98% rename from libc/stdio/posix_spawnp.c rename to libc/proc/posix_spawnp.c index 941b47c9a..44edf6579 100644 --- a/libc/stdio/posix_spawnp.c +++ b/libc/proc/posix_spawnp.c @@ -19,7 +19,7 @@ #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/limits.h" -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" /** * Spawns process the POSIX way w/ PATH search. diff --git a/libc/proc/proc.c b/libc/proc/proc.c new file mode 100644 index 000000000..4565e8b90 --- /dev/null +++ b/libc/proc/proc.c @@ -0,0 +1,192 @@ +/*-*- 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/calls/internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/cosmo.h" +#include "libc/intrin/dll.h" +#include "libc/intrin/leaky.internal.h" +#include "libc/intrin/weaken.h" +#include "libc/mem/mem.h" +#include "libc/nt/accounting.h" +#include "libc/nt/enum/processcreationflags.h" +#include "libc/nt/enum/status.h" +#include "libc/nt/enum/wait.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/proc/proc.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/errfuns.h" +#include "libc/thread/tls.h" +#ifdef __x86_64__ + +struct Procs __proc; + +static textwindows dontinstrument uint32_t __proc_worker(void *arg) { + __bootstrap_tls(&__proc.tls, __builtin_frame_address(0)); + for (;;) { + int64_t handles[64]; + int sic, dosignal = 0; + struct Proc *pr, *objects[64]; + struct Dll *e, *e2, *samples = 0; + uint32_t millis, status, i, n = 1; + __proc_lock(); + handles[0] = __proc.onstart; + for (e = dll_first(__proc.list); e && n < 64; ++n, e = e2) { + pr = PROC_CONTAINER(e); + e2 = dll_next(__proc.list, e); + dll_remove(&__proc.list, e); + dll_make_last(&samples, e); + handles[n] = pr->handle; + objects[n] = pr; + } + dll_make_last(&__proc.list, samples); + __proc_unlock(); + millis = n == 64 ? __SIG_PROC_INTERVAL_MS : -1u; + i = WaitForMultipleObjects(n, handles, false, millis); + i &= ~kNtWaitAbandoned; + if (!i || i == kNtWaitTimeout) continue; + GetExitCodeProcess(handles[i], &status); + if (status == kNtStillActive) continue; + __proc_lock(); + pr = objects[i]; + if (status == 0xc9af3d51u) status = kNtStillActive; + pr->wstatus = status; + if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN || + (__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) && + (!pr->waiters && !__proc.waiters)) { + CloseHandle(pr->handle); + dll_remove(&__proc.list, &pr->elem); + dll_make_first(&__proc.free, &pr->elem); + } else { + pr->iszombie = 1; + dll_remove(&__proc.list, &pr->elem); + dll_make_first(&__proc.zombies, &pr->elem); + if (pr->waiters) { + nsync_cv_broadcast(&pr->onexit); + } else if (__proc.waiters) { + nsync_cv_signal(&__proc.onexit); + } else { + dosignal = 1; + sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED; + } + } + __proc_unlock(); + if (dosignal) { + __sig_generate(SIGCHLD, sic); + } + } + return 0; +} + +/** + * Lazy initializes process tracker data structures and worker. + */ +static textwindows void __proc_setup(void) { + __proc.onstart = CreateSemaphore(0, 0, 1, 0); + __proc.thread = CreateThread(0, 65536, __proc_worker, 0, + kNtStackSizeParamIsAReservation, 0); +} + +/** + * Locks process tracker. + */ +textwindows void __proc_lock(void) { + cosmo_once(&__proc.once, __proc_setup); + nsync_mu_lock(&__proc.lock); +} + +/** + * Unlocks process tracker. + */ +textwindows void __proc_unlock(void) { + nsync_mu_unlock(&__proc.lock); +} + +/** + * Resets process tracker from forked child. + */ +textwindows void __proc_wipe(void) { + bzero(&__proc, sizeof(__proc)); +} + +/** + * Allocates object for new process. + * + * The returned memory is not tracked by any list. It must be filled in + * with system process information and then added back to the system by + * calling __proc_add(). If process creation fails, then it needs to be + * released using __proc_free(). + */ +textwindows struct Proc *__proc_new(void) { + struct Dll *e; + struct Proc *proc = 0; + int i, n = ARRAYLEN(__proc.pool); + if (atomic_load_explicit(&__proc.allocated, memory_order_relaxed) < n && + (i = atomic_fetch_add(&__proc.allocated, 1)) < n) { + proc = __proc.pool + i; + } else { + if ((e = dll_first(__proc.free))) { + proc = PROC_CONTAINER(e); + dll_remove(&__proc.free, &proc->elem); + } + if (!proc) { + if (_weaken(malloc)) { + proc = _weaken(malloc)(sizeof(struct Proc)); + } else { + enomem(); + return 0; + } + } + } + if (proc) { + bzero(proc, sizeof(*proc)); + dll_init(&proc->elem); + } + return proc; +} + +IGNORE_LEAKS(__proc_new) + +/** + * Adds process to active list. + * + * The handle and pid must be filled in before calling this. + */ +textwindows void __proc_add(struct Proc *proc) { + dll_make_first(&__proc.list, &proc->elem); + ReleaseSemaphore(__proc.onstart, 1, 0); +} + +/** + * Frees process allocation. + * + * Process must not be currently tracked in the active or zombies list. + */ +textwindows void __proc_free(struct Proc *proc) { + dll_make_first(&__proc.free, &proc->elem); +} + +#endif /* __x86_64__ */ diff --git a/libc/proc/proc.h b/libc/proc/proc.h new file mode 100755 index 000000000..e69de29bb diff --git a/libc/proc/proc.internal.h b/libc/proc/proc.internal.h new file mode 100644 index 000000000..80baf820f --- /dev/null +++ b/libc/proc/proc.internal.h @@ -0,0 +1,53 @@ +#ifndef COSMOPOLITAN_LIBC_PROC_H_ +#define COSMOPOLITAN_LIBC_PROC_H_ +#include "libc/atomic.h" +#include "libc/calls/struct/rusage.h" +#include "libc/intrin/dll.h" +#include "libc/thread/thread.h" +#include "libc/thread/tls.h" +#include "third_party/nsync/cv.h" +#include "third_party/nsync/mu.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define PROC_CONTAINER(e) DLL_CONTAINER(struct Proc, elem, e) + +struct Proc { + int pid; + int waiters; + bool iszombie; + bool wasforked; + uint32_t wstatus; + int64_t handle; + struct Dll elem; + nsync_cv onexit; +}; + +struct Procs { + int waiters; + atomic_uint once; + nsync_mu lock; + nsync_cv onexit; + intptr_t thread; + intptr_t onstart; + struct Dll *list; + struct Dll *free; + struct Dll *zombies; + struct Proc pool[8]; + unsigned allocated; + struct CosmoTib tls; +}; + +extern struct Procs __proc; + +void __proc_wipe(void); +void __proc_lock(void); +void __proc_unlock(void); +struct Proc *__proc_new(void); +void __proc_add(struct Proc *); +void __proc_free(struct Proc *); +int sys_wait4_nt(int, int *, int, struct rusage *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_PROC_H_ */ diff --git a/libc/proc/proc.mk b/libc/proc/proc.mk new file mode 100644 index 000000000..420ac911f --- /dev/null +++ b/libc/proc/proc.mk @@ -0,0 +1,70 @@ +#-*-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_PROC + +LIBC_PROC_ARTIFACTS += LIBC_PROC_A +LIBC_PROC = $(LIBC_PROC_A_DEPS) $(LIBC_PROC_A) +LIBC_PROC_A = o/$(MODE)/libc/proc/proc.a +LIBC_PROC_A_FILES := $(wildcard libc/proc/*) $(wildcard libc/proc/unlocked/*) +LIBC_PROC_A_HDRS = $(filter %.h,$(LIBC_PROC_A_FILES)) +LIBC_PROC_A_SRCS_S = $(filter %.S,$(LIBC_PROC_A_FILES)) +LIBC_PROC_A_SRCS_C = $(filter %.c,$(LIBC_PROC_A_FILES)) + +LIBC_PROC_A_SRCS = \ + $(LIBC_PROC_A_SRCS_S) \ + $(LIBC_PROC_A_SRCS_C) + +LIBC_PROC_A_OBJS = \ + $(LIBC_PROC_A_SRCS_S:%.S=o/$(MODE)/%.o) \ + $(LIBC_PROC_A_SRCS_C:%.c=o/$(MODE)/%.o) + +LIBC_PROC_A_CHECKS = \ + $(LIBC_PROC_A).pkg \ + $(LIBC_PROC_A_HDRS:%=o/$(MODE)/%.ok) + +LIBC_PROC_A_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_NT_KERNEL32 \ + LIBC_NT_PSAPI \ + LIBC_RUNTIME \ + LIBC_STR \ + LIBC_SYSV \ + LIBC_SYSV_CALLS \ + THIRD_PARTY_NSYNC \ + THIRD_PARTY_NSYNC_MEM + +LIBC_PROC_A_DEPS := \ + $(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x)))) + +$(LIBC_PROC_A):libc/proc/ \ + $(LIBC_PROC_A).pkg \ + $(LIBC_PROC_A_OBJS) + +$(LIBC_PROC_A).pkg: \ + $(LIBC_PROC_A_OBJS) \ + $(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x)_A).pkg) + +$(LIBC_PROC_A_OBJS): private \ + COPTS += \ + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + +# aarch64 friendly assembly code +o/$(MODE)/libc/proc/vfork.o: libc/proc/vfork.S + @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< + +LIBC_PROC_LIBS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x))) +LIBC_PROC_SRCS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_SRCS)) +LIBC_PROC_HDRS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_HDRS)) +LIBC_PROC_CHECKS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_CHECKS)) +LIBC_PROC_OBJS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_OBJS)) +$(LIBC_PROC_OBJS): $(BUILD_FILES) libc/proc/proc.mk + +.PHONY: o/$(MODE)/libc/proc +o/$(MODE)/libc/proc: $(LIBC_PROC_CHECKS) diff --git a/libc/stdio/system.c b/libc/proc/system.c similarity index 100% rename from libc/stdio/system.c rename to libc/proc/system.c diff --git a/libc/stdio/systemvpe.c b/libc/proc/systemvpe.c similarity index 100% rename from libc/stdio/systemvpe.c rename to libc/proc/systemvpe.c diff --git a/libc/runtime/vfork.S b/libc/proc/vfork.S similarity index 71% rename from libc/runtime/vfork.S rename to libc/proc/vfork.S index 80f1bb79d..9bf801b8b 100644 --- a/libc/runtime/vfork.S +++ b/libc/proc/vfork.S @@ -23,30 +23,15 @@ // Forks process without copying page tables. // -// This is the same as fork() except it's optimized for the case -// where the caller invokes execve() immediately afterwards. You -// can also call functions like close(), dup2(), etc. Call _exit -// but don't call exit. Look for vforksafe function annotations, -// For example pthread mutexes are @vforksafe because they don't -// do anything in a vfork()'d child process. TLS memory must not -// be disabled (it's enabled by default) since vfork() needs it. -// -// What makes vfork() dangerous is that any changes to memory in -// the child process can happen in the parent too. The exception -// to this rule is `errno` which is saved/restored in a register -// by this implementation. However, despite its dangers, vfork's -// performance is irresistible and wonderous to behold. If safer -// code is desired, consider posix_spawn() which uses vfork(). -// -// Do not make the assumption that the parent is suspended until -// the child terminates since this uses the raw fork system call -// on Windows, OpenBSD, and MacOS. In that case the process will -// proceed without blocking the parent; however, the `__vforked` -// variable is still set to true in the child, so lock functions -// won't do anything, and other functions shall change behavior. -// This ensures that, even if the operating system does not give -// us the performance of vfork(), we'll still be able to cut out -// the libc overhead, e.g. pthread_atfork(). +// This function lets a process spawn another process without +// copying the page tables. The parent process gets suspended +// until the child calls either execve() or _Exit(), and they +// share memory during that time. That's at least how we want +// vfork() to work. Support for these behaviors is patchy. It +// is also error-prone to use this function in an environment +// with signal handlers and threads. The best way to use this +// is by calling posix_spawn() which works around the dangers +// and determines at runtime the best way to pass error codes // // @return pid of child process or 0 if forked process // @returnstwice @@ -71,7 +56,6 @@ vfork: #if !IsTiny() push %rbp mov %rsp,%rbp - call __require_tls #ifdef SYSDEBUG ezlea .Llog,di call __stracef @@ -118,11 +102,11 @@ vfork: // } else { // __get_tls()->tib_flags &= ~TIB_FLAG_VFORKED; // } - sub x1,x28,#1152 - ldr x2,[x1,0x40] + sub x1,x28,#0x80 // RELIES ON TLS TIB ABI! + ldr x2,[x1,64] cbnz x0,2f orr x2,x2,#TIB_FLAG_VFORKED -1: str x2,[x1,0x40] +1: str x2,[x1,64] b 3f 2: and x2,x2,#~TIB_FLAG_VFORKED b 1b diff --git a/libc/calls/wait.c b/libc/proc/wait.c similarity index 100% rename from libc/calls/wait.c rename to libc/proc/wait.c diff --git a/libc/calls/wait3.c b/libc/proc/wait3.c similarity index 100% rename from libc/calls/wait3.c rename to libc/proc/wait3.c diff --git a/libc/proc/wait4-nt.c b/libc/proc/wait4-nt.c new file mode 100644 index 000000000..47a7e688b --- /dev/null +++ b/libc/proc/wait4-nt.c @@ -0,0 +1,172 @@ +/*-*- 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 │ +│ │ +│ 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/assert.h" +#include "libc/calls/bo.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/struct/rusage.h" +#include "libc/calls/struct/timespec.h" +#include "libc/cosmo.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/intrin/dll.h" +#include "libc/intrin/strace.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/accounting.h" +#include "libc/nt/process.h" +#include "libc/nt/runtime.h" +#include "libc/nt/struct/filetime.h" +#include "libc/nt/struct/processmemorycounters.h" +#include "libc/proc/proc.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/w.h" +#include "libc/sysv/errfuns.h" +#include "libc/thread/tls.h" + +#ifdef __x86_64__ + +static textwindows void GetProcessStats(int64_t h, struct rusage *ru) { + bzero(ru, sizeof(*ru)); + struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)}; + unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount))); + ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024; + ru->ru_majflt = memcount.PageFaultCount; + struct NtFileTime createtime, exittime; + struct NtFileTime kerneltime, usertime; + unassert(GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime)); + ru->ru_utime = WindowsDurationToTimeVal(ReadFileTime(usertime)); + ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime)); + struct NtIoCounters iocount; + unassert(GetProcessIoCounters(h, &iocount)); + ru->ru_inblock = iocount.ReadOperationCount; + ru->ru_oublock = iocount.WriteOperationCount; +} + +static textwindows struct timespec GetNextDeadline(struct timespec deadline) { + if (__tls_enabled && __get_tls()->tib_sigmask == -1) return timespec_max; + if (timespec_iszero(deadline)) deadline = timespec_real(); + struct timespec delay = timespec_frommillis(__SIG_PROC_INTERVAL_MS); + return timespec_add(deadline, delay); +} + +static textwindows int ReapZombie(struct Proc *pr, int *wstatus, + struct rusage *opt_out_rusage) { + if (wstatus) { + *wstatus = pr->wstatus; + } + if (opt_out_rusage) { + GetProcessStats(pr->handle, opt_out_rusage); + } + if (!pr->waiters) { + CloseHandle(pr->handle); + dll_remove(&__proc.zombies, &pr->elem); + dll_make_first(&__proc.free, &pr->elem); + } + return pr->pid; +} + +static textwindows int CheckZombies(int pid, int *wstatus, + struct rusage *opt_out_rusage) { + struct Dll *e; + for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) { + struct Proc *pr = PROC_CONTAINER(e); + if (pid == -1 && pr->waiters) { + continue; // this zombie has been claimed + } + if (pid == -1 || pid == pr->pid) { + return ReapZombie(pr, wstatus, opt_out_rusage); + } + } + return 0; +} + +static textwindows int WaitForProcess(int pid, int *wstatus, int options, + struct rusage *opt_out_rusage) { + int rc, *wv; + nsync_cv *cv; + struct Dll *e; + struct Proc *pr; + struct timespec deadline = timespec_zero; + + // check list of processes that've already exited + if ((rc = CheckZombies(pid, wstatus, opt_out_rusage))) { + return rc; + } + + // find the mark + pr = 0; + if (pid == -1) { + if (dll_is_empty(__proc.list)) { + return echild(); + } + cv = &__proc.onexit; + wv = &__proc.waiters; + } else { + for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) { + if (pid == PROC_CONTAINER(e)->pid) { + pr = PROC_CONTAINER(e); + } + } + if (pr) { + unassert(!pr->iszombie); + cv = &pr->onexit; + wv = &pr->waiters; + } else { + return echild(); + } + } + + // wait for status change + if (options & WNOHANG) return 0; +CheckForInterrupt: + if (_check_interrupts(kSigOpRestartable) == -1) return -1; + deadline = GetNextDeadline(deadline); +SpuriousWakeup: + BEGIN_BLOCKING_OPERATION; + ++*wv; + rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0); + --*wv; + END_BLOCKING_OPERATION; + if (rc == ECANCELED) return ecanceled(); + if (pr && pr->iszombie) return ReapZombie(pr, wstatus, opt_out_rusage); + if (rc == ETIMEDOUT) goto CheckForInterrupt; + unassert(!rc); + if (!pr && (rc = CheckZombies(pid, wstatus, opt_out_rusage))) return rc; + goto SpuriousWakeup; +} + +textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, + struct rusage *opt_out_rusage) { + int rc; + if (options & ~WNOHANG) { + return einval(); // no support for WCONTINUED and WUNTRACED yet + } + // XXX: NT doesn't really have process groups. For instance the + // CreateProcess() flag for starting a process group actually + // just does an "ignore ctrl-c" internally. + if (pid == 0) pid = -1; + if (pid < -1) pid = -pid; + __proc_lock(); + rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage); + __proc_unlock(); + return rc; +} + +#endif /* __x86_64__ */ diff --git a/libc/calls/wait4-sysv.c b/libc/proc/wait4-sysv.c similarity index 100% rename from libc/calls/wait4-sysv.c rename to libc/proc/wait4-sysv.c diff --git a/libc/calls/wait4.c b/libc/proc/wait4.c similarity index 98% rename from libc/calls/wait4.c rename to libc/proc/wait4.c index cf2bff5dd..cd9652bdd 100644 --- a/libc/calls/wait4.c +++ b/libc/proc/wait4.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/wait4.h" #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" #include "libc/calls/struct/rusage.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/proc/proc.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/calls/waitpid.c b/libc/proc/waitpid.c similarity index 100% rename from libc/calls/waitpid.c rename to libc/proc/waitpid.c diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 3688adc8d..c6ca37d8e 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -33,6 +33,8 @@ #include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/macros.internal.h" +#include "libc/mem/alloca.h" +#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/runtime.h" #include "libc/nt/signals.h" #include "libc/nt/synchronization.h" @@ -40,10 +42,12 @@ #include "libc/nt/thunk/msabi.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/syslib.internal.h" #include "libc/sock/internal.h" #include "libc/stdalign.internal.h" #include "libc/str/str.h" +#include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/futex.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/nrlinux.h" @@ -85,6 +89,8 @@ struct CloneArgs { void *arg; }; +int __stack_call(void *, int, long, long, int (*)(void *, int), void *); + static struct CloneArgs *AllocateCloneArgs(char *stk, size_t stksz) { return (struct CloneArgs *)(((uintptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & @@ -100,29 +106,15 @@ __msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue; __msabi extern typeof(ExitThread) *const __imp_ExitThread; __msabi extern typeof(WakeByAddressAll) *const __imp_WakeByAddressAll; -int WinThreadLaunch(void *arg, // rdi - int tid, // rsi - int (*func)(void *, int), // rdx - intptr_t rsp); // rcx - -// we can't log this function because: -// 1. windows owns the backtrace pointer right now -// 2. ftrace unwinds rbp to determine depth -// 3. tid in tls for ftrace isn't set yet -// we can't use address sanitizer because: -// 1. __asan_handle_no_return wipes stack -// 2. windows owns the stack memory right now -// we need win32 raw imports because: -// 1. generated thunks are function logged -dontasan dontinstrument static textwindows wontreturn void // -WinThreadEntry(int rdi, // rcx - int rsi, // rdx - int rdx, // r8 - struct CloneArgs *wt) { // r9 +static textwindows dontinstrument wontreturn void // +WinThreadEntry(int rdi, // rcx + int rsi, // rdx + int rdx, // r8 + struct CloneArgs *wt) { // r9 int rc; if (wt->tls) __set_tls_win32(wt->tls); *wt->ctid = wt->tid; - rc = WinThreadLaunch(wt->arg, wt->tid, wt->func, (intptr_t)wt); + rc = __stack_call(wt->arg, wt->tid, 0, 0, wt->func, wt); // we can now clear ctid directly since we're no longer using our own // stack memory, which can now be safely free'd by the parent thread. *wt->ztid = 0; @@ -130,7 +122,7 @@ WinThreadEntry(int rdi, // rcx // since we didn't indirect this function through NT2SYSV() it's not // safe to simply return, and as such, we need ExitThread(). __imp_ExitThread(rc); - notpossible; + __builtin_unreachable(); } static textwindows errno_t CloneWindows(int (*func)(void *, int), char *stk, @@ -144,8 +136,12 @@ static textwindows errno_t CloneWindows(int (*func)(void *, int), char *stk, wt->func = func; wt->arg = arg; wt->tls = flags & CLONE_SETTLS ? tls : 0; - if ((h = CreateThread(0, 65536, (void *)WinThreadEntry, wt, 0, &wt->utid))) { - CloseHandle(h); + if ((h = CreateThread(0, 65536, (void *)WinThreadEntry, wt, + kNtStackSizeParamIsAReservation, &wt->utid))) { + if (flags & CLONE_SETTLS) { + struct CosmoTib *tib = tls; + tib->tib_syshand = h; + } if (flags & CLONE_PARENT_SETTID) { *ptid = wt->tid; } @@ -196,9 +192,6 @@ XnuThreadMain(void *pthread, // rdi func(arg, tid); - // avoid signal handler being triggered after we trash our stack - _sigblockall(); - // we no longer use the stack after this point // %rax = int bsdthread_terminate(%rdi = void *stackaddr, // %rsi = size_t freesize, @@ -210,7 +203,7 @@ XnuThreadMain(void *pthread, // rdi : "=m"(*wt->ztid) : "a"(0x2000000 | 361), "D"(0), "S"(0), "d"(0L) : "rcx", "r10", "r11", "memory"); - notpossible; + __builtin_unreachable(); } static errno_t CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, @@ -236,8 +229,6 @@ static wontreturn void FreebsdThreadMain(void *p) { struct CloneArgs *wt = p; *wt->ctid = wt->tid; wt->func(wt->arg, wt->tid); - // avoid signal handler being triggered after we trash our stack - _sigblockall(); // we no longer use the stack after this point // void thr_exit(%rdi = long *state); asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 @@ -250,7 +241,7 @@ static wontreturn void FreebsdThreadMain(void *p) { : "=m"(*wt->ztid) : "a"(454), "D"(wt->ztid), "S"(UMTX_OP_WAKE), "d"(INT_MAX) : "rcx", "r8", "r9", "r10", "r11", "memory"); - notpossible; + __builtin_unreachable(); } static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, @@ -290,7 +281,7 @@ static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, // we can't use address sanitizer because: // 1. __asan_handle_no_return wipes stack [todo?] -dontasan static wontreturn void OpenbsdThreadMain(void *p) { +static wontreturn void OpenbsdThreadMain(void *p) { struct CloneArgs *wt = p; *wt->ctid = wt->tid; wt->func(wt->arg, wt->tid); @@ -304,7 +295,7 @@ dontasan static wontreturn void OpenbsdThreadMain(void *p) { : "a"(83), "m"(__oldstack), "D"(wt->ztid), "S"(2 /* FUTEX_WAKE */), "d"(INT_MAX) : "rcx", "r11", "memory"); - notpossible; + __builtin_unreachable(); } static errno_t CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, @@ -353,8 +344,6 @@ static wontreturn void NetbsdThreadMain(void *arg, // rdi ax = sys_gettid(); *ctid = ax; func(arg, ax); - // avoid signal handler being triggered after we trash our stack - _sigblockall(); // we no longer use the stack after this point // %eax = int __lwp_exit(void); asm volatile("movl\t$0,%2\n\t" // *wt->ztid = 0 @@ -362,7 +351,7 @@ static wontreturn void NetbsdThreadMain(void *arg, // rdi : "=a"(ax), "=d"(dx), "=m"(*ztid) : "0"(310) : "rcx", "r11", "memory"); - notpossible; + __builtin_unreachable(); } static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, @@ -449,21 +438,10 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, // APPLE SILICON static void *SiliconThreadMain(void *arg) { - register struct CloneArgs *wt asm("x21") = arg; + struct CloneArgs *wt = arg; + asm volatile("mov\tx28,%0" : /* no outputs */ : "r"(wt->tls)); *wt->ctid = wt->this; - register void *x0 asm("x0") = wt->arg; - register int x1 asm("w1") = wt->this; - register void *x28 asm("x28") = wt->tls; - asm volatile("mov\tx19,x29\n\t" // save frame pointer - "mov\tx20,sp\n\t" // save stack pointer - "mov\tx29,#0\n\t" // reset backtrace - "mov\tsp,%3\n\t" // switch stack - "blr\t%2\n\t" // wt->func(wt->arg, tid) - "mov\tx29,x19\n\t" // restore frame pointer - "mov\tsp,x20" // restore stack pointer - : "+r"(x0) - : "r"(x1), "r"(wt->func), "r"(wt), "r"(x28) - : "x19", "x20", "memory"); + __stack_call(wt->arg, wt->this, 0, 0, wt->func, wt); *wt->ztid = 0; return 0; } @@ -471,9 +449,11 @@ static void *SiliconThreadMain(void *arg) { static errno_t CloneSilicon(int (*fn)(void *, int), char *stk, size_t stksz, int flags, void *arg, void *tls, int *ptid, int *ctid) { + void *attr; errno_t res; unsigned tid; pthread_t th; + size_t babystack; struct CloneArgs *wt; static atomic_uint tids; wt = AllocateCloneArgs(stk, stksz); @@ -484,14 +464,23 @@ static errno_t CloneSilicon(int (*fn)(void *, int), char *stk, size_t stksz, wt->tls = flags & CLONE_SETTLS ? tls : 0; wt->func = fn; wt->arg = arg; - if (!(res = __syslib->pthread_create(&th, 0, SiliconThreadMain, wt)) && + babystack = __syslib->__pthread_stack_min; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + attr = alloca(__syslib->__sizeof_pthread_attr_t); +#pragma GCC pop_options + unassert(!__syslib->__pthread_attr_init(attr)); + unassert(!__syslib->__pthread_attr_setguardsize(attr, 0)); + unassert(!__syslib->__pthread_attr_setstacksize(attr, babystack)); + if (!(res = __syslib->__pthread_create(&th, attr, SiliconThreadMain, wt)) && (flags & CLONE_PARENT_SETTID)) { *ptid = tid; if (flags & CLONE_SETTLS) { struct CosmoTib *tib = tls; - tib[-1].tib_pthread = th; + tib[-1].tib_syshand = th; } } + unassert(!__syslib->__pthread_attr_destroy(attr)); return res; } @@ -620,9 +609,10 @@ static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz, * child has started. * * - `CLONE_PARENT_SETTID` must be specified if you intend to set - * the `ptid` argument, which is updated at the most opportune - * moment. On all platforms except XNU, this happens before - * clone() returns. On XNU, it happens once the thread starts. + * the `ptid` argument, and it is updated at the most opportune + * moment. On all platforms except XNU x86, this happens before + * clone() returns. But since it might not be available yet you + * need to use pthread_getunique_np() to obtain it. * * - `CLONE_CHILD_CLEARTID` causes `*ctid = 0` upon child thread * termination. This is used to implement join so that the parent @@ -653,9 +643,6 @@ errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg, if (!func) { rc = EINVAL; - } else if (!IsTiny() && - ((flags & CLONE_VM) && (stksz < 4096 || (stksz & 15)))) { - rc = EINVAL; } else if (IsAsan() && (((flags & CLONE_SETTLS) && !__asan_is_valid(tls, 64)) || ((flags & CLONE_PARENT_SETTID) && diff --git a/libc/runtime/cosmo2.c b/libc/runtime/cosmo2.c index 9ce1c100f..965aee20e 100644 --- a/libc/runtime/cosmo2.c +++ b/libc/runtime/cosmo2.c @@ -84,6 +84,17 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) { // its used by --strace and also kprintf() %T kStartTsc = rdtsc(); + // enable enough tls to survive until it can be allocated properly + struct CosmoTib tib = { + .tib_self = &tib, + .tib_self2 = &tib, + .tib_sigmask = -1, + .tib_sigstack_size = 57344, + .tib_sigstack_addr = (char *)__builtin_frame_address(0) - 57344, + .tib_tid = 1, + }; + __set_tls(&tib); + // extracts arguments from old sysv stack abi int argc = *sp; char **argv = (char **)(sp + 1); @@ -118,7 +129,7 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) { } // check system call abi compatibility - if (SupportsXnu() && __syslib && __syslib->version < SYSLIB_VERSION) { + if (SupportsXnu() && __syslib && __syslib->__version < SYSLIB_VERSION) { sys_write(2, "need newer ape loader\n", 22); _Exit(127); } diff --git a/libc/runtime/efimain.greg.c b/libc/runtime/efimain.greg.c index 7f421f35b..4e953be01 100644 --- a/libc/runtime/efimain.greg.c +++ b/libc/runtime/efimain.greg.c @@ -141,7 +141,7 @@ static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) { * * @see libc/dce.h */ -__msabi dontasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle, +__msabi EFI_STATUS EfiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { struct mman *mm; uint32_t DescVersion; diff --git a/libc/runtime/enable_threads.c b/libc/runtime/enable_threads.c deleted file mode 100644 index a7b342ffb..000000000 --- a/libc/runtime/enable_threads.c +++ /dev/null @@ -1,63 +0,0 @@ -/*-*- 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 "ape/sections.internal.h" -#include "libc/calls/syscall-sysv.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/runtime/runtime.h" -#include "libc/thread/tls.h" - -extern int __threadcalls_end[] __attribute__((__weak__)); -extern int __threadcalls_start[] __attribute__((__weak__)); - -static privileged void FixupLockNops(void) { - __morph_begin(); - /* - * _NOPL("__threadcalls", func) - * - * The big ugly macro above is used by Cosmopolitan Libc to unser - * locking primitive (e.g. flockfile, funlockfile) have zero impact on - * performance and binary size when threads aren't actually in play. - * - * 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 - * - * This is cheap and fast because the big ugly macro stored in the - * binary the offsets of all the instructions we need to change. - */ - for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) { - __executable_start[*p + 0] = 0x67; - __executable_start[*p + 1] = 0x67; - __executable_start[*p + 2] = 0xe8; - } - __morph_end(); -} - -void __enable_threads(void) { - if (__threaded) return; -#ifdef __x86_64__ - STRACE("__enable_threads()"); - FixupLockNops(); -#endif - __threaded = __tls_enabled ? __get_tls()->tib_tid : sys_gettid(); -} diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 184705bfa..a82569643 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -27,20 +27,25 @@ #include "libc/intrin/dll.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/syslib.internal.h" #include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" +#include "third_party/make/gnumake.h" #define I(x) ((uintptr_t)x) extern unsigned char __tls_mov_nt_rax[]; extern unsigned char __tls_add_nt_rax[]; -static struct PosixThread _pthread_main; _Alignas(TLS_ALIGNMENT) static char __static_tls[6016]; /** @@ -191,7 +196,15 @@ textstartup void __enable_tls(void) { tib->tib_strace = __strace; tib->tib_ftrace = __ftrace; tib->tib_locale = (intptr_t)&__c_dot_utf8_locale; - tib->tib_pthread = (pthread_t)&_pthread_main; + tib->tib_pthread = (pthread_t)&_pthread_static; + if (IsWindows()) { + intptr_t threadhand, pseudo = GetCurrentThread(); + DuplicateHandle(GetCurrentProcess(), pseudo, GetCurrentProcess(), + &threadhand, 0, false, kNtDuplicateSameAccess); + atomic_store_explicit(&tib->tib_syshand, threadhand, memory_order_relaxed); + } else if (IsXnuSilicon()) { + tib->tib_syshand = __syslib->__pthread_self(); + } if (IsLinux() || IsXnuSilicon()) { // gnu/systemd guarantees pid==tid for the main thread so we can // avoid issuing a superfluous system call at startup in program @@ -202,11 +215,14 @@ textstartup void __enable_tls(void) { atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); // initialize posix threads - _pthread_main.tib = tib; - _pthread_main.flags = PT_STATIC; - _pthread_main.list.prev = _pthread_main.list.next = // - _pthread_list = __veil("r", &_pthread_main.list); - atomic_store_explicit(&_pthread_main.ptid, tid, memory_order_relaxed); + _pthread_static.tib = tib; + _pthread_static.pt_flags = PT_STATIC; + dll_init(&_pthread_static.list); + _pthread_list = &_pthread_static.list; + atomic_store_explicit(&_pthread_static.ptid, tid, memory_order_relaxed); + if (IsWindows()) { + npassert((_pthread_static.semaphore = CreateSemaphore(0, 0, 1, 0))); + } // copy in initialized data section if (I(_tdata_size)) { diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index 364c7d7e7..43ff46910 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -17,7 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" +#include "libc/atomic.h" +#include "libc/calls/blockcancel.internal.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" +#include "libc/cosmo.h" #include "libc/elf/tinyelf.internal.h" #include "libc/errno.h" #include "libc/intrin/bits.h" @@ -29,12 +33,19 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" -static bool IsMyDebugBinaryImpl(const char *path) { - int fd; +static struct { + atomic_uint once; + const char *res; + char buf[PATH_MAX]; +} g_comdbg; + +static bool IsMyDebugBinary(const char *path) { void *map; int64_t size; uintptr_t value; bool res = false; + int fd, e = errno; + BLOCK_CANCELLATIONS; if ((fd = open(path, O_RDONLY | O_CLOEXEC, 0)) != -1) { // sanity test that this .com.dbg file (1) is an elf image, and (2) // contains the same number of bytes of code as our .com executable @@ -49,16 +60,26 @@ static bool IsMyDebugBinaryImpl(const char *path) { } close(fd); } + ALLOW_CANCELLATIONS; + errno = e; return res; } -static bool IsMyDebugBinary(const char *path) { - int e; - bool res; - e = errno; - res = IsMyDebugBinaryImpl(path); - errno = e; - return res; +static void FindDebugBinaryInit(void) { + char *p = GetProgramExecutableName(); + size_t n = strlen(p); + if ((n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) || + IsMyDebugBinary(p)) { + g_comdbg.res = p; + } else if (n + 4 < ARRAYLEN(g_comdbg.buf)) { + mempcpy(mempcpy(g_comdbg.buf, p, n), ".dbg", 5); + if (IsMyDebugBinary(g_comdbg.buf)) { + g_comdbg.res = g_comdbg.buf; + } + } + if (!g_comdbg.res) { + g_comdbg.res = getenv("COMDBG"); + } } /** @@ -67,27 +88,6 @@ static bool IsMyDebugBinary(const char *path) { * @return path to debug binary, or NULL */ const char *FindDebugBinary(void) { - static bool once; - static char *res; - static char buf[PATH_MAX]; - char *p; - size_t n; - if (!once) { - p = GetProgramExecutableName(); - n = strlen(p); - if ((n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) || - IsMyDebugBinary(p)) { - res = p; - } else if (n + 4 < ARRAYLEN(buf)) { - mempcpy(mempcpy(buf, p, n), ".dbg", 5); - if (IsMyDebugBinary(buf)) { - res = buf; - } - } - if (!res) { - res = getenv("COMDBG"); - } - once = true; - } - return res; + cosmo_once(&g_comdbg.once, FindDebugBinaryInit); + return g_comdbg.res; } diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 184443f97..b5caee0af 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -26,6 +26,8 @@ #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" #include "libc/thread/tls2.internal.h" @@ -74,30 +76,39 @@ __funline int GetNestingLevel(struct CosmoFtrace *ft, struct StackFrame *sf) { * @see ftrace_install() */ privileged void ftracer(void) { - uintptr_t fn; long stackuse; + uintptr_t fn, st; struct CosmoTib *tib; struct StackFrame *sf; struct CosmoFtrace *ft; + struct PosixThread *pt; + sf = __builtin_frame_address(0); + st = (uintptr_t)__argv - sizeof(uintptr_t); if (__ftrace <= 0) return; if (__tls_enabled) { tib = __get_tls_privileged(); if (tib->tib_ftrace <= 0) return; ft = &tib->tib_ftracer; + if ((char *)sf >= tib->tib_sigstack_addr && + (char *)sf <= tib->tib_sigstack_addr + tib->tib_sigstack_size) { + st = (uintptr_t)tib->tib_sigstack_addr + tib->tib_sigstack_size; + } else if ((pt = (struct PosixThread *)tib->tib_pthread) && + pt->attr.__stacksize) { + st = (uintptr_t)pt->attr.__stackaddr + pt->attr.__stacksize; + } } else { ft = &g_ftrace; } + stackuse = st - (intptr_t)sf; if (_cmpxchg(&ft->ft_once, false, true)) { ft->ft_lastaddr = -1; - ft->ft_skew = GetNestingLevelImpl(__builtin_frame_address(0)); + ft->ft_skew = GetNestingLevelImpl(sf); } if (_cmpxchg(&ft->ft_noreentry, false, true)) { - sf = __builtin_frame_address(0); sf = sf->next; fn = sf->addr + DETOUR_SKEW; if (fn != ft->ft_lastaddr) { - stackuse = GetStackAddr() + GetStackSize() - (intptr_t)sf; - kprintf("%rFUN %6P %'13T %'*ld %*s%t\n", ftrace_stackdigs, stackuse, + kprintf("%rFUN %6P %'16T %'*ld %*s%t\n", ftrace_stackdigs, stackuse, GetNestingLevel(ft, sf) * 2, "", fn); ft->ft_lastaddr = fn; } diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index 7e9892cfc..97f58911e 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -35,7 +35,7 @@ struct DosArgv { wint_t wc; }; -textwindows dontasan void DecodeDosArgv(int ignore, struct DosArgv *st) { +textwindows void DecodeDosArgv(int ignore, struct DosArgv *st) { wint_t x, y; for (;;) { if (!(x = *st->s++)) break; @@ -51,7 +51,7 @@ textwindows dontasan void DecodeDosArgv(int ignore, struct DosArgv *st) { st->wc = x; } -static textwindows dontasan void AppendDosArgv(wint_t wc, struct DosArgv *st) { +static textwindows void AppendDosArgv(wint_t wc, struct DosArgv *st) { uint64_t w; w = tpenc(wc); do { @@ -60,7 +60,7 @@ static textwindows dontasan void AppendDosArgv(wint_t wc, struct DosArgv *st) { } while (w >>= 8); } -static textwindows dontasan int Count(int c, struct DosArgv *st) { +static textwindows int Count(int c, struct DosArgv *st) { int ignore, n = 0; asm("" : "=g"(ignore)); while (st->wc == c) { @@ -87,7 +87,7 @@ static textwindows dontasan int Count(int c, struct DosArgv *st) { // @see test/libc/dosarg_test.c // @see libc/runtime/ntspawn.c // @note kudos to Simon Tatham for figuring out quoting behavior -textwindows dontasan int GetDosArgv(const char16_t *cmdline, char *buf, +textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, char **argv, size_t max) { bool inquote; int i, argc, slashes, quotes, ignore; diff --git a/libc/runtime/getdosenviron.c b/libc/runtime/getdosenviron.c index ddd386ed5..4744c1ace 100644 --- a/libc/runtime/getdosenviron.c +++ b/libc/runtime/getdosenviron.c @@ -21,13 +21,15 @@ #include "libc/str/str.h" #include "libc/str/utf16.h" +#define abi textwindows dontinstrument + #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) -forceinline int IsAlpha(int c) { +__funline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } -forceinline char *MemChr(const char *s, unsigned char c, unsigned long n) { +__funline char *MemChr(const char *s, unsigned char c, unsigned long n) { for (; n; --n, ++s) { if ((*s & 255) == c) { return (char *)s; @@ -36,8 +38,7 @@ forceinline char *MemChr(const char *s, unsigned char c, unsigned long n) { return 0; } -static textwindows dontasan dontinstrument axdx_t -Recode16to8(char *dst, size_t dstsize, const char16_t *src) { +static abi axdx_t Recode16to8(char *dst, size_t dstsize, const char16_t *src) { bool v; axdx_t r; uint64_t w; @@ -70,7 +71,7 @@ Recode16to8(char *dst, size_t dstsize, const char16_t *src) { return r; } -textwindows dontinstrument dontasan void FixPath(char *path) { +static abi void FixPath(char *path) { char *p; // turn backslash into slash @@ -109,9 +110,8 @@ textwindows dontinstrument dontasan void FixPath(char *path) { // @param envp stores NULL-terminated string pointer list (optional) // @param max is the pointer count capacity of envp // @return number of variables decoded, excluding NULL-terminator -textwindows dontasan dontinstrument int GetDosEnviron(const char16_t *env, - char *buf, size_t size, - char **envp, size_t max) { +abi int GetDosEnviron(const char16_t *env, char *buf, size_t size, char **envp, + size_t max) { int i; char *p; axdx_t r; diff --git a/libc/runtime/getmemtracksize.c b/libc/runtime/getmemtracksize.c index 3b6746d63..6762ff123 100644 --- a/libc/runtime/getmemtracksize.c +++ b/libc/runtime/getmemtracksize.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/memtrack.internal.h" -dontasan size_t __get_memtrack_size(struct MemoryIntervals *mm) { +size_t __get_memtrack_size(struct MemoryIntervals *mm) { size_t i, n; for (n = i = 0; i < mm->i; ++i) { n += ((size_t)(mm->p[i].y - mm->p[i].x) + 1) << 16; diff --git a/libc/runtime/getsymbolbyaddr.c b/libc/runtime/getsymbolbyaddr.c index 9416e13e5..576280d9b 100644 --- a/libc/runtime/getsymbolbyaddr.c +++ b/libc/runtime/getsymbolbyaddr.c @@ -23,7 +23,7 @@ /** * Returns name of symbol at address. */ -dontasan char *GetSymbolByAddr(int64_t addr) { +char *GetSymbolByAddr(int64_t addr) { /* asan runtime depends on this function */ int i; struct SymbolTable *st; diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index f3ffdb96b..2fb4ec6ec 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -44,8 +44,8 @@ int GetDosEnviron(const char16_t *, char *, size_t, char **, size_t); bool __intercept_flag(int *, char *[], const char *); int sys_mprotect_nt(void *, size_t, int); int __inflate(void *, size_t, const void *, size_t); -void *__mmap_unlocked(void *, size_t, int, int, int, int64_t) dontasan; -int __munmap_unlocked(char *, size_t) dontasan; +void *__mmap_unlocked(void *, size_t, int, int, int, int64_t); +int __munmap_unlocked(char *, size_t); void __on_arithmetic_overflow(void); void __init_fds(int, char **, char **); diff --git a/libc/runtime/isheap.c b/libc/runtime/isheap.c index a13b77bcd..1eb5fded0 100644 --- a/libc/runtime/isheap.c +++ b/libc/runtime/isheap.c @@ -26,7 +26,7 @@ * @assume stack addresses are always greater than heap addresses * @assume stack memory isn't stored beneath %rsp (-mno-red-zone) */ -optimizesize dontasan bool _isheap(void *p) { +optimizesize bool _isheap(void *p) { intptr_t x, y; x = kAutomapStart; y = x + kAutomapSize; diff --git a/libc/runtime/isstackoverflow.c b/libc/runtime/isstackoverflow.c new file mode 100644 index 000000000..c31b80f2f --- /dev/null +++ b/libc/runtime/isstackoverflow.c @@ -0,0 +1,63 @@ +/*-*- 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/struct/siginfo.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/sig.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" +#include "libc/thread/tls.h" + +/** + * Returns true if signal is likely a stack overflow. + */ +char __is_stack_overflow(siginfo_t *si, void *ucontext) { + + // check if signal has the information we need + ucontext_t *uc = ucontext; + if (!si) return false; + if (!uc) return false; + if (si->si_signo != SIGSEGV && si->si_signo != SIGBUS) return false; + + // with threads we know exactly where the guard page is + int pagesz = getauxval(AT_PAGESZ); + uintptr_t addr = (uintptr_t)si->si_addr; + struct PosixThread *pt = _pthread_self(); + if (pt->attr.__stacksize) { + uintptr_t stack = (uintptr_t)pt->attr.__stackaddr; + uintptr_t guard = pt->attr.__guardsize; + uintptr_t bot, top; + if (guard) { + bot = stack; + top = bot + guard; + } else { + bot = stack - pagesz; + top = stack; + } + return addr >= bot && addr < top; + } + + // it's easy to guess with the main stack + // even though it's hard to know its exact boundaries + uintptr_t sp = uc->uc_mcontext.SP; + return addr <= sp && addr >= sp - pagesz; +} diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 807e1d509..d7faa73e0 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -43,10 +43,8 @@ struct MemoryIntervals { extern struct MemoryIntervals _mmi; -void __mmi_init(void); void __mmi_lock(void); void __mmi_unlock(void); -void __mmi_funlock(void); bool IsMemtracked(int, int); void PrintSystemMappings(int); unsigned __find_memory(const struct MemoryIntervals *, int) nosideeffect; diff --git a/libc/runtime/memtracknt.c b/libc/runtime/memtracknt.c index b6889a28f..a207c852d 100644 --- a/libc/runtime/memtracknt.c +++ b/libc/runtime/memtracknt.c @@ -23,14 +23,14 @@ #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" -static inline dontasan void *GetFrameAddr(int f) { +static inline void *GetFrameAddr(int f) { intptr_t a; a = f; a *= FRAMESIZE; return (void *)a; } -dontasan void __release_memory_nt(struct MemoryIntervals *mm, int l, int r) { +void __release_memory_nt(struct MemoryIntervals *mm, int l, int r) { int i; for (i = l; i <= r; ++i) { UnmapViewOfFile(GetFrameAddr(mm->p[i].x)); diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 2670cfe94..e7afe843c 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -77,7 +77,7 @@ static wontreturn void __mmap_die(const char *s) { _Exit(199); } -static dontasan inline bool __overlaps_existing_mapping(char *p, size_t n) { +static inline bool __overlaps_existing_mapping(char *p, size_t n) { int a, b, i; unassert(n > 0); a = FRAME(p); @@ -91,7 +91,7 @@ static dontasan inline bool __overlaps_existing_mapping(char *p, size_t n) { return false; } -static dontasan bool __choose_memory(int x, int n, int align, int *res) { +static bool __choose_memory(int x, int n, int align, int *res) { // TODO: improve performance int i, start, end; unassert(align > 0); @@ -152,12 +152,12 @@ static dontasan bool __choose_memory(int x, int n, int align, int *res) { return false; } -dontasan static bool __auto_map(int count, int align, int *res) { +static bool __auto_map(int count, int align, int *res) { return __choose_memory(FRAME(kAutomapStart), count, align, res) && *res + count <= FRAME(kAutomapStart + (kAutomapSize - 1)); } -static dontasan void *__finish_memory(void *addr, size_t size, int prot, +static void *__finish_memory(void *addr, size_t size, int prot, int flags, int fd, int64_t off, int f, int x, int n, struct DirectMap dm) { if (!IsWindows() && (flags & MAP_FIXED)) { @@ -178,7 +178,7 @@ static dontasan void *__finish_memory(void *addr, size_t size, int prot, return addr; } -static dontasan void *__map_memory(void *addr, size_t size, int prot, int flags, +static void *__map_memory(void *addr, size_t size, int prot, int flags, int fd, int64_t off, int f, int x, int n) { struct DirectMap dm; dm = sys_mmap(addr, size, prot, f, fd, off); @@ -200,7 +200,7 @@ static dontasan void *__map_memory(void *addr, size_t size, int prot, int flags, * This is useful on Windows since it allows us to partially unmap or * punch holes into existing mappings. */ -static textwindows dontinline dontasan void *__map_memories( +static textwindows dontinline void *__map_memories( char *addr, size_t size, int prot, int flags, int fd, int64_t off, int f, int x, int n) { size_t i, m; @@ -238,14 +238,13 @@ static textwindows dontinline dontasan void *__map_memories( return addr; } -dontasan inline void *__mmap_unlocked(void *addr, size_t size, int prot, +inline void *__mmap_unlocked(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { - int a, f, n, x; char *p = addr; struct DirectMap dm; size_t requested_size; bool needguard, clashes; - unsigned long page_size; + int a, f, n, x, pagesize; size_t virtualused, virtualneed; if (VERY_UNLIKELY(!size)) { @@ -273,7 +272,7 @@ dontasan inline void *__mmap_unlocked(void *addr, size_t size, int prot, } requested_size = size; - page_size = getauxval(AT_PAGESZ); + pagesize = getauxval(AT_PAGESZ); if (flags & MAP_ANONYMOUS) { fd = -1; off = 0; @@ -288,7 +287,7 @@ dontasan inline void *__mmap_unlocked(void *addr, size_t size, int prot, } else if (VERY_UNLIKELY(off < 0)) { STRACE("mmap negative offset"); return VIP(einval()); - } else if (off & ((IsWindows() ? FRAMESIZE : page_size) - 1)) { + } else if (off & ((IsWindows() ? FRAMESIZE : pagesize) - 1)) { STRACE("mmap offset isn't properly aligned"); return VIP(einval()); } else if (VERY_UNLIKELY(INT64_MAX - size < off)) { @@ -358,31 +357,21 @@ dontasan inline void *__mmap_unlocked(void *addr, size_t size, int prot, // starting page and the bottom guard page, since that would stop // our stack page from growing down. npassert(!sys_munmap(p, size)); - // by default MAP_GROWSDOWN will auto-allocate 10mb of pages. it's - // supposed to stop growing if an adjacent allocation exists, to - // prevent your stacks from overlapping on each other. we're not - // able to easily assume a mapping beneath this one exists. even - // if we could, the linux kernel requires for muh security reasons - // that stacks be at least 1mb away from each other, so it's not - // possible to avoid this call if our goal is to have 60kb stacks - // with 4kb guards like a sane multithreaded production system. - // however this 1mb behavior oddly enough is smart enough to not - // apply if the mapping is a manually-created guard page. - int e = errno; - if ((dm = sys_mmap(p + size - SIGSTKSZ, SIGSTKSZ, prot, + int guardsize = pagesize, e = errno; + if ((dm = sys_mmap(p + size - guardsize, guardsize, prot, f | MAP_GROWSDOWN_linux, fd, off)) .addr != MAP_FAILED) { - npassert(sys_mmap(p, GetGuardSize(), PROT_NONE, + npassert(sys_mmap(p, pagesize, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) .addr == p); dm.addr = p; p = __finish_memory(p, size, prot, flags, fd, off, f, x, n, dm); if (IsAsan() && p != MAP_FAILED) { - __asan_poison(p, GetGuardSize(), kAsanStackOverflow); + __asan_poison(p, pagesize, kAsanStackOverflow); } return p; } else if (errno == ENOTSUP) { - // WSL doesn't support MAP_GROWSDOWN + // WSL and Blink don't support MAP_GROWSDOWN needguard = true; errno = e; } @@ -406,9 +395,9 @@ dontasan inline void *__mmap_unlocked(void *addr, size_t size, int prot, kAsanMmapSizeOverrun); } if (needguard) { - unassert(!mprotect(p, page_size, PROT_NONE)); + unassert(!mprotect(p, pagesize, PROT_NONE)); if (IsAsan()) { - __asan_poison(p, page_size, kAsanStackOverflow); + __asan_poison(p, pagesize, kAsanStackOverflow); } } } diff --git a/libc/runtime/msync-nt.c b/libc/runtime/msync-nt.c index ece63a8e1..dcf592226 100644 --- a/libc/runtime/msync-nt.c +++ b/libc/runtime/msync-nt.c @@ -25,7 +25,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" -dontasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { +textwindows int sys_msync_nt(char *addr, size_t size, int flags) { int i, rc = 0; char *a, *b, *x, *y; __mmi_lock(); diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index b482e0952..0a0d35aba 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -39,7 +39,7 @@ #define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) #define FRAME(x) ((int)((intptr_t)(x) >> 16)) -static dontasan void __munmap_shadow(char *p, size_t n) { +static void __munmap_shadow(char *p, size_t n) { intptr_t a, b, x, y; KERNTRACE("__munmap_shadow(%p, %'zu)", p, n); a = ((intptr_t)p >> 3) + 0x7fff8000; @@ -66,7 +66,7 @@ static dontasan void __munmap_shadow(char *p, size_t n) { // our api supports doing things like munmap(0, 0x7fffffffffff) but some // platforms (e.g. openbsd) require that we know the specific intervals // or else it returns EINVAL. so we munmap a piecewise. -static dontasan void __munmap_impl(char *p, size_t n) { +static void __munmap_impl(char *p, size_t n) { char *q; size_t m; intptr_t a, b, c; @@ -112,7 +112,7 @@ static dontasan void __munmap_impl(char *p, size_t n) { } } -dontasan int __munmap_unlocked(char *p, size_t n) { +int __munmap_unlocked(char *p, size_t n) { unassert(!__vforked); if (UNLIKELY(!n)) { STRACE("munmap n is 0"); diff --git a/libc/runtime/printargs.c b/libc/runtime/printargs.c index 673303ea8..60b4f4488 100644 --- a/libc/runtime/printargs.c +++ b/libc/runtime/printargs.c @@ -82,7 +82,7 @@ static const char *FindNameById(const struct IdName *names, unsigned long id) { return NULL; } -static dontasan void PrintDependencies(const char *prologue) { +static void PrintDependencies(const char *prologue) { struct NtLinkedList *head = &NtGetPeb()->Ldr->InLoadOrderModuleList; struct NtLinkedList *ldr = head->Next; do { @@ -93,10 +93,10 @@ static dontasan void PrintDependencies(const char *prologue) { } while ((ldr = ldr->Next) && ldr != head); } -static dontasan void Print(const char *prologue) { +static void Print(const char *prologue) { } -static dontasan const char *ConvertCcToStr(int cc) { +static const char *ConvertCcToStr(int cc) { if (cc == _POSIX_VDISABLE) { return "_POSIX_VDISABLE"; } else { @@ -115,7 +115,16 @@ static dontasan const char *ConvertCcToStr(int cc) { * * @param prologue needs to be a .rodata kprintf string */ -dontasan textstartup void __printargs(const char *prologue) { +textstartup void __printargs(const char *prologue) { + +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + union { + char path[PATH_MAX]; + struct pollfd pfds[128]; + } u; + CheckLargeStackAllocation(&u, sizeof(u)); +#pragma GCC pop_options const struct AuxiliaryValue { const char *fmt; @@ -172,10 +181,6 @@ dontasan textstartup void __printargs(const char *prologue) { struct sched_param sp; struct termios termios; const struct AuxiliaryValue *auxinfo; - union { - char path[PATH_MAX]; - struct pollfd pfds[128]; - } u; (void)x; @@ -275,7 +280,9 @@ dontasan textstartup void __printargs(const char *prologue) { if (X86_HAVE(LA57)) kprintf(" LA57"); if (X86_HAVE(FSGSBASE)) kprintf(" FSGSBASE"); #elif defined(__aarch64__) - PRINT(" AARCH64"); + kprintf(" AARCH64\n"); +#else + kprintf("\n"); #endif PRINT(""); @@ -391,6 +398,7 @@ dontasan textstartup void __printargs(const char *prologue) { PRINT(" ☼ %p __oldstack ptr", __oldstack); PRINT(" ☼ %p __oldstack bot", ROUNDDOWN(__oldstack, foss_stack_size)); PRINT(" ☼ %p __builtin_frame_address(0)", __builtin_frame_address(0)); + PRINT(" ☼ %p GetStackPointer()", GetStackPointer()); PRINT(""); PRINT("ARGUMENTS (%p)", __argv); @@ -448,7 +456,7 @@ dontasan textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %d", "geteuid()", geteuid()); PRINT(" ☼ %s = %d", "getgid()", getgid()); PRINT(" ☼ %s = %d", "getegid()", getegid()); - PRINT(" ☼ %s = %#s", "kTmpPath", kTmpPath); + PRINT(" ☼ %s = %#s", "__get_tmpdir()", __get_tmpdir()); #ifdef __x86_64__ PRINT(" ☼ %s = %#s", "kNtSystemDirectory", kNtSystemDirectory); PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 2f9b4110a..edfb47eb9 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -79,7 +79,6 @@ extern int __strace; extern int __ftrace; extern uint64_t __syscount; extern uint64_t kStartTsc; -extern char kTmpPath[]; extern const char kNtSystemDirectory[]; extern const char kNtWindowsDirectory[]; extern size_t __virtualmax; @@ -138,6 +137,10 @@ int __get_arg_max(void) pureconst; int __get_cpu_count(void) pureconst; long __get_avphys_pages(void) pureconst; long __get_phys_pages(void) pureconst; +long __get_minsigstksz(void) pureconst; +void __get_main_stack(void **, size_t *, int *); +long __get_safe_size(long, long); +char *__get_tmpdir(void); #endif /* _COSMO_SOURCE */ COSMOPOLITAN_C_END_ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 181b068c6..451ebba90 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -68,43 +68,11 @@ $(LIBC_RUNTIME_A).pkg: \ o/$(MODE)/libc/runtime/cosmo2.o: private \ CFLAGS += -O0 -o/$(MODE)/libc/runtime/ftracer.o: private \ - CFLAGS += \ - -x-no-pg \ - -ffreestanding \ - -fno-sanitize=all - -o/$(MODE)/libc/runtime/cosmo2.o \ -o/$(MODE)/libc/runtime/fork-nt.o \ -o/$(MODE)/libc/runtime/enable_tls.o \ -o/$(MODE)/libc/runtime/printmemoryintervals.o \ -o/$(MODE)/libc/runtime/findmemoryinterval.o \ -o/$(MODE)/libc/runtime/sys_mprotect.greg.o \ -o/$(MODE)/libc/runtime/getdosargv.o \ -o/$(MODE)/libc/runtime/getdosenviron.o \ -o/$(MODE)/libc/runtime/hook.greg.o \ -o/$(MODE)/libc/runtime/ismemtracked.greg.o \ -o/$(MODE)/libc/runtime/memtracknt.o \ -o/$(MODE)/libc/runtime/memtrack.greg.o \ -o/$(MODE)/libc/runtime/metalprintf.greg.o \ -o/$(MODE)/libc/runtime/printargs.greg.o \ -o/$(MODE)/libc/runtime/mman.greg.o \ -o/$(MODE)/libc/runtime/print.greg.o \ -o/$(MODE)/libc/runtime/stackchkfail.o \ -o/$(MODE)/libc/runtime/stackchkfaillocal.o \ -o/$(MODE)/libc/runtime/winmain.greg.o \ -o/$(MODE)/libc/runtime/interceptflag.greg.o \ -o/$(MODE)/libc/runtime/opensymboltable.o: private \ - CFLAGS += \ - -Os \ - -ffreestanding \ - $(NO_MAGIC) - -# must use alloca() -# can't use asan or any runtime services -o/$(MODE)/libc/runtime/fork-nt.o: private \ - CPPFLAGS += \ - -DSTACK_FRAME_UNLIMITED +$(LIBC_RUNTIME_A_OBJS): private \ + COPTS += \ + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 o/$(MODE)/libc/runtime/qsort.o: private \ CFLAGS += \ @@ -125,22 +93,6 @@ o/$(MODE)/libc/runtime/enable_tls.o: private \ -mcmodel=large endif -# privileged functions -o/$(MODE)/libc/runtime/morph.o \ -o/$(MODE)/libc/runtime/getsymbol.o \ -o/$(MODE)/libc/runtime/enable_threads.o \ -o/$(MODE)/libc/runtime/morph_tls.o: private \ - CFLAGS += \ - -ffreestanding \ - -fno-sanitize=all \ - -fno-stack-protector - -# TODO(jart): We need a way to avoid WinThreadEntry() being hooked. -o/$(MODE)/libc/runtime/clone.o: private \ - COPTS += \ - -fno-sanitize=all \ - -fpatchable-function-entry=0,0 - o/$(MODE)/libc/runtime/.cosmo.zip.o: private \ ZIPOBJ_FLAGS += \ -B @@ -150,8 +102,6 @@ o/$(MODE)/libc/runtime/init.o: libc/runtime/init.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/runtime/wipe.o: libc/runtime/wipe.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/runtime/vfork.o: libc/runtime/vfork.S - @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/runtime/clone-linux.o: libc/runtime/clone-linux.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/runtime/ftrace-hook.o: libc/runtime/ftrace-hook.S diff --git a/libc/runtime/set_tls.c b/libc/runtime/set_tls.c index 20a5eae49..3cabed830 100644 --- a/libc/runtime/set_tls.c +++ b/libc/runtime/set_tls.c @@ -34,7 +34,6 @@ textstartup void __set_tls(struct CosmoTib *tib) { #ifdef __x86_64__ // ask the operating system to change the x86 segment register if (IsWindows()) { - __tls_index = TlsAlloc(); npassert(0 <= __tls_index && __tls_index < 64); asm("mov\t%1,%%gs:%0" : "=m"(*((long *)0x1480 + __tls_index)) : "r"(tib)); } else if (IsFreebsd()) { diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index 438a46e17..4d04850c1 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -5,8 +5,6 @@ /** * Returns preferred size and alignment of thread stack. - * - * This will always be equal to `PTHREAD_STACK_MIN`. */ #define GetStackSize() 262144 @@ -73,8 +71,7 @@ extern char ape_stack_align[] __attribute__((__weak__)); * process too, then you'll need STATIC_STACK_ALIGN(GetStackSize()) * which will burn O(256kb) of memory to ensure thread invariants. */ -#define GetStackAddr() \ - (((intptr_t)__builtin_frame_address(0) - 1) & -GetStackSize()) +#define GetStackAddr() ((GetStackPointer() - 1) & -GetStackSize()) #define GetStaticStackSize() ((uintptr_t)ape_stack_memsz) @@ -86,9 +83,8 @@ extern char ape_stack_align[] __attribute__((__weak__)); * which will burn O(256kb) of memory to ensure thread invariants, * which make this check exceedingly fast. */ -#define HaveStackMemory(n) \ - ((intptr_t)__builtin_frame_address(0) >= \ - GetStackAddr() + GetGuardSize() + (n)) +#define HaveStackMemory(n) \ + (GetStackPointer() >= GetStackAddr() + GetGuardSize() + (n)) /** * Extends stack memory by poking large allocations. @@ -146,6 +142,19 @@ int FreeCosmoStack(void *) libcesque; #define GetStaticStackAddr(ADDEND) (GetStackAddr() + ADDEND) #endif +#define GetStackPointer() \ + ({ \ + uintptr_t __sp; \ + __asm__(__mov_sp : "=r"(__sp)); \ + __sp; \ + }) + +#ifdef __x86_64__ +#define __mov_sp "mov\t%%rsp,%0" +#elif defined(__aarch64__) +#define __mov_sp "mov\t%0,sp" +#endif + COSMOPOLITAN_C_END_ #endif /* GNU ELF */ #endif /* _COSMO_SOURCE */ diff --git a/libc/runtime/sysconf.c b/libc/runtime/sysconf.c index 1c44c784b..17b695d80 100644 --- a/libc/runtime/sysconf.c +++ b/libc/runtime/sysconf.c @@ -30,6 +30,7 @@ #include "libc/sysv/consts/_posix.h" #include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/ss.h" #include "libc/sysv/errfuns.h" #include "libc/thread/thread.h" @@ -40,6 +41,8 @@ * * - `_SC_CLK_TCK` returns number of clock ticks per second * - `_SC_ARG_MAX` will perform expensive rlimit calculations + * - `_SC_SIGSTKSZ` returns host platform's preferred SIGSTKSZ + * - `_SC_MINSIGSTKSZ` returns host platform's required MINSIGSTKSZ * - `_SC_PAGESIZE` currently always returns 65536 due to Windows * - `_SC_AVPHYS_PAGES` returns average physical memory pages * - `_SC_PHYS_PAGES` returns physical memory pages available @@ -58,6 +61,10 @@ long sysconf(int name) { return FRAMESIZE; case _SC_ARG_MAX: return __get_arg_max(); + case _SC_SIGSTKSZ: + return _SIGSTKSZ; + case _SC_MINSIGSTKSZ: + return __get_minsigstksz(); case _SC_CHILD_MAX: return __get_rlimit(RLIMIT_NPROC); case _SC_OPEN_MAX: diff --git a/libc/runtime/sysconf.h b/libc/runtime/sysconf.h index 6c2e05cf9..6da479458 100644 --- a/libc/runtime/sysconf.h +++ b/libc/runtime/sysconf.h @@ -142,6 +142,8 @@ #define _SC_XOPEN_STREAMS 246 #define _SC_THREAD_ROBUST_PRIO_INHERIT 247 #define _SC_THREAD_ROBUST_PRIO_PROTECT 248 +#define _SC_SIGSTKSZ 249 +#define _SC_MINSIGSTKSZ 250 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/runtime/syslib.internal.h b/libc/runtime/syslib.internal.h index d58037913..7942ad6ce 100644 --- a/libc/runtime/syslib.internal.h +++ b/libc/runtime/syslib.internal.h @@ -1,9 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_SYSLIB_H_ #define COSMOPOLITAN_LIBC_RUNTIME_SYSLIB_H_ -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/timespec.h" -#include "libc/thread/thread.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -16,35 +12,43 @@ COSMOPOLITAN_C_START_ */ #define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24) -#define SYSLIB_VERSION 2 +#define SYSLIB_VERSION 3 typedef uint64_t dispatch_time_t; typedef uint64_t dispatch_semaphore_t; struct Syslib { - int magic; - int version; - long (*fork)(void); - long (*pipe)(int[2]); - long (*clock_gettime)(int, struct timespec *); - long (*nanosleep)(const struct timespec *, struct timespec *); - long (*mmap)(void *, size_t, int, int, int, int64_t); - int (*pthread_jit_write_protect_supported_np)(void); - void (*pthread_jit_write_protect_np)(int); - void (*sys_icache_invalidate)(void *, size_t); - int (*pthread_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), - void *); - void (*pthread_exit)(void *); - int (*pthread_kill)(pthread_t, int); - int (*pthread_sigmask)(int, const sigset_t *restrict, sigset_t *restrict); - int (*pthread_setname_np)(const char *); - dispatch_semaphore_t (*dispatch_semaphore_create)(long); - long (*dispatch_semaphore_signal)(dispatch_semaphore_t); - long (*dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t); - dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t); + int __magic; + int __version; + long (*__fork)(void); + long (*__pipe)(int[2]); + long (*__clock_gettime)(int, void *); + long (*__nanosleep)(const void *, void *); + long (*__mmap)(void *, size_t, int, int, int, int64_t); + int (*__pthread_jit_write_protect_supported_np)(void); + void (*__pthread_jit_write_protect_np)(int); + void (*__sys_icache_invalidate)(void *, size_t); + int (*__pthread_create)(void *, const void *, void *(*)(void *), void *); + void (*__pthread_exit)(void *); + int (*__pthread_kill)(long, int); + int (*__pthread_sigmask)(int, const void *restrict, void *restrict); + int (*__pthread_setname_np)(const char *); + dispatch_semaphore_t (*__dispatch_semaphore_create)(long); + long (*__dispatch_semaphore_signal)(dispatch_semaphore_t); + long (*__dispatch_semaphore_wait)(dispatch_semaphore_t, dispatch_time_t); + dispatch_time_t (*__dispatch_walltime)(const void *, int64_t); /* v2 (2023-09-10) */ - long (*pthread_self)(void); - void (*dispatch_release)(dispatch_semaphore_t); + long (*__pthread_self)(void); + void (*__dispatch_release)(dispatch_semaphore_t); + int (*__raise)(int); + int (*__pthread_join)(long, void **); + void (*__pthread_yield_np)(void); + int __pthread_stack_min; + int __sizeof_pthread_attr_t; + int (*__pthread_attr_init)(void *); + int (*__pthread_attr_destroy)(void *); + int (*__pthread_attr_setstacksize)(void *, size_t); + int (*__pthread_attr_setguardsize)(void *, size_t); }; extern struct Syslib *__syslib; diff --git a/libc/runtime/warnifpowersave.c b/libc/runtime/warnifpowersave.c index bc3ceaff1..42027cb4b 100644 --- a/libc/runtime/warnifpowersave.c +++ b/libc/runtime/warnifpowersave.c @@ -16,9 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" // RDTSC on Linux has so much jitter when the CPU is in powersave mode. @@ -31,12 +36,18 @@ "/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor\n" void __warn_if_powersave(void) { - int fd; + int e, fd; char buf[16] = {0}; - if (!fileexists(FILE)) return; - if ((fd = open(FILE, O_RDONLY)) == -1) return; - read(fd, buf, 15); - close(fd); - if (!startswith(buf, "powersave")) return; - write(2, WARN, sizeof(WARN) - 1); + if (IsLinux()) { + e = errno; + BLOCK_CANCELLATIONS; + if ((fd = __sys_openat(AT_FDCWD, FILE, O_RDONLY, 0)) != -1) { + sys_read(fd, buf, 15); + sys_close(fd); + if (!startswith(buf, "powersave")) return; + sys_write(2, WARN, sizeof(WARN) - 1); + } + ALLOW_CANCELLATIONS; + errno = e; + } } diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 7d1325310..084d86777 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -18,36 +18,22 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" -#include "libc/calls/state.internal.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" -#include "libc/intrin/asancodes.h" -#include "libc/intrin/describeflags.internal.h" -#include "libc/intrin/getenv.internal.h" #include "libc/intrin/weaken.h" +#include "libc/limits.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nt/console.h" -#include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/consolemodeflags.h" -#include "libc/nt/enum/creationdisposition.h" -#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filemapflags.h" -#include "libc/nt/enum/filesharemode.h" #include "libc/nt/enum/pageflags.h" #include "libc/nt/files.h" -#include "libc/nt/ipc.h" #include "libc/nt/memory.h" #include "libc/nt/pedef.internal.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" -#include "libc/nt/signals.h" -#include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/struct/teb.h" -#include "libc/nt/synchronization.h" -#include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" @@ -60,13 +46,13 @@ #ifdef __x86_64__ +#define abi __msabi textwindows dontinstrument + // clang-format off __msabi extern typeof(CreateFileMapping) *const __imp_CreateFileMappingW; __msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle; -__msabi extern typeof(ExitProcess) *const __imp_ExitProcess; __msabi extern typeof(FreeEnvironmentStrings) *const __imp_FreeEnvironmentStringsW; __msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode; -__msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess; __msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId; __msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW; __msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; @@ -80,9 +66,9 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; // clang-format on void cosmo(int, char **, char **, long (*)[2]) wontreturn; -void __switch_stacks(int, char **, char **, long (*)[2], - void (*)(int, char **, char **, long (*)[2]), - intptr_t) wontreturn; +void __stack_call(int, char **, char **, long (*)[2], + void (*)(int, char **, char **, long (*)[2]), + intptr_t) wontreturn; static const signed char kNtStdio[3] = { (signed char)kNtStdInputHandle, @@ -90,18 +76,12 @@ static const signed char kNtStdio[3] = { (signed char)kNtStdErrorHandle, }; -forceinline int IsAlpha(int c) { +__funline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } -// implements all win32 apis on non-windows hosts -__msabi long __oops_win32(void) { - assert(!"win32 api called on non-windows host"); - return 0; -} - // https://nullprogram.com/blog/2022/02/18/ -__msabi static inline char16_t *MyCommandLine(void) { +__funline char16_t *MyCommandLine(void) { void *cmd; asm("mov\t%%gs:(0x60),%0\n" "mov\t0x20(%0),%0\n" @@ -110,8 +90,14 @@ __msabi static inline char16_t *MyCommandLine(void) { return cmd; } +// implements all win32 apis on non-windows hosts +static abi long __oops_win32(void) { + assert(!"win32 api called on non-windows host"); + return 0; +} + // returns true if utf-8 path is a win32-style path that exists -__msabi static textwindows bool32 WinFileExists(const char *path) { +static abi bool32 WinFileExists(const char *path) { uint16_t path16[PATH_MAX]; size_t z = ARRAYLEN(path16); size_t n = tprecode8to16(path16, z, path).ax; @@ -120,14 +106,14 @@ __msabi static textwindows bool32 WinFileExists(const char *path) { } // this ensures close(1) won't accidentally close(2) for example -__msabi static textwindows void DeduplicateStdioHandles(void) { +static abi void DeduplicateStdioHandles(void) { for (long i = 0; i < 3; ++i) { int64_t h1 = __imp_GetStdHandle(kNtStdio[i]); for (long j = i + 1; j < 3; ++j) { int64_t h2 = __imp_GetStdHandle(kNtStdio[j]); if (h1 == h2) { - int64_t h3, proc = __imp_GetCurrentProcess(); - __imp_DuplicateHandle(proc, h2, proc, &h3, 0, false, + int64_t h3; + __imp_DuplicateHandle(-1, h2, -1, &h3, 0, false, kNtDuplicateSameAccess); __imp_SetStdHandle(kNtStdio[j], h3); } @@ -137,7 +123,7 @@ __msabi static textwindows void DeduplicateStdioHandles(void) { // main function of windows init process // i.e. first process spawned that isn't forked -__msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) { +static abi wontreturn void WinInit(const char16_t *cmdline) { __oldstack = (intptr_t)__builtin_frame_address(0); // make console into utf-8 ansi/xterm style tty @@ -177,26 +163,6 @@ __msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) { struct WinArgs *wa = (struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs))); - // allocate asan memory if needed - if (IsAsan()) { - uintptr_t shadowaddr = 0x7fff8000 + (stackaddr >> 3); - uintptr_t shadowend = 0x7fff8000 + ((stackaddr + stacksize) >> 3); - uintptr_t shallocaddr = ROUNDDOWN(shadowaddr, FRAMESIZE); - uintptr_t shallocend = ROUNDUP(shadowend, FRAMESIZE); - uintptr_t shallocsize = shallocend - shallocaddr; - __imp_MapViewOfFileEx( - (_mmi.p[1].h = __imp_CreateFileMappingW( - -1, 0, kNtPageReadwrite, shallocsize >> 32, shallocsize, NULL)), - kNtFileMapWrite, 0, 0, shallocsize, (void *)shallocaddr); - _mmi.p[1].x = shallocaddr >> 16; - _mmi.p[1].y = (shallocaddr >> 16) + ((shallocsize - 1) >> 16); - _mmi.p[1].prot = PROT_READ | PROT_WRITE; - _mmi.p[1].flags = 0x00000022; // private+anonymous - _mmi.p[1].size = shallocsize; - _mmi.i = 2; - __asan_poison((void *)stackaddr, GetGuardSize(), kAsanStackOverflow); - } - // parse utf-16 command into utf-8 argv array in argument block int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock), wa->argv, ARRAYLEN(wa->argv)); @@ -235,12 +201,12 @@ __msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) { __envp = &wa->envp[0]; // handover control to cosmopolitan runtime - __switch_stacks(count, wa->argv, wa->envp, wa->auxv, cosmo, - stackaddr + (stacksize - sizeof(struct WinArgs))); + __stack_call(count, wa->argv, wa->envp, wa->auxv, cosmo, + stackaddr + (stacksize - sizeof(struct WinArgs))); } -__msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, - const char *lpCmdLine, int64_t nCmdShow) { +abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, + const char *lpCmdLine, int64_t nCmdShow) { const char16_t *cmdline; extern char os asm("__hostos"); os = _HOSTWINDOWS; // madness https://news.ycombinator.com/item?id=21019722 diff --git a/libc/runtime/zipos-mmap.c b/libc/runtime/zipos-mmap.c index 1c8fd9351..e1396c5d1 100644 --- a/libc/runtime/zipos-mmap.c +++ b/libc/runtime/zipos-mmap.c @@ -49,7 +49,7 @@ * it does not need to be 64kb aligned. * @return virtual base address of new mapping, or MAP_FAILED w/ errno */ -dontasan void *__zipos_mmap(void *addr, size_t size, int prot, int flags, +void *__zipos_mmap(void *addr, size_t size, int prot, int flags, struct ZiposHandle *h, int64_t off) { if (off < 0) { diff --git a/libc/runtime/zipos-open.c b/libc/runtime/zipos-open.c index da289f5a6..cb6ec3c6d 100644 --- a/libc/runtime/zipos-open.c +++ b/libc/runtime/zipos-open.c @@ -52,6 +52,10 @@ static char *__zipos_mapend; static size_t __zipos_maptotal; static pthread_mutex_t __zipos_lock_obj; +static void __zipos_wipe(void) { + pthread_mutex_init(&__zipos_lock_obj, 0); +} + static void __zipos_lock(void) { pthread_mutex_lock(&__zipos_lock_obj); } @@ -60,10 +64,6 @@ static void __zipos_unlock(void) { pthread_mutex_unlock(&__zipos_lock_obj); } -static void __zipos_funlock(void) { - pthread_mutex_init(&__zipos_lock_obj, 0); -} - static void *__zipos_mmap_space(size_t mapsize) { char *start; size_t offset; @@ -268,5 +268,6 @@ int __zipos_open(struct ZiposUri *name, int flags) { } __attribute__((__constructor__)) static void __zipos_ctor(void) { - pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_funlock); + __zipos_wipe(); + pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_wipe); } diff --git a/libc/runtime/zipos-seek.c b/libc/runtime/zipos-seek.c index cc89fca1e..7ce82b408 100644 --- a/libc/runtime/zipos-seek.c +++ b/libc/runtime/zipos-seek.c @@ -19,51 +19,34 @@ #include "libc/calls/calls.h" #include "libc/runtime/zipos.internal.h" #include "libc/stdckdint.h" -#include "libc/sysv/consts/s.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/zip.internal.h" -static int64_t __zipos_seek_impl(struct ZiposHandle *h, int64_t offset, - unsigned whence) { - int64_t pos; - if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY || - S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) { - return eisdir(); - } +static int64_t GetPosition(struct ZiposHandle *h, int whence) { switch (whence) { case SEEK_SET: - if (offset >= 0) { - return offset; - } else { - return einval(); - } + return 0; case SEEK_CUR: - if (!ckd_add(&pos, h->pos, offset)) { - if (pos >= 0) { - return pos; - } else { - return einval(); - } - } else { - return eoverflow(); - } + return h->pos; case SEEK_END: - if (!ckd_sub(&pos, h->size, offset)) { - if (pos >= 0) { - return pos; - } else { - return einval(); - } - } else { - return eoverflow(); - } + return h->size; default: return einval(); } } +static int64_t Seek(struct ZiposHandle *h, int64_t offset, int whence) { + int64_t pos; + if (!ckd_add(&pos, GetPosition(h, whence), offset)) { + if (pos >= 0) { + return pos; + } else { + return einval(); + } + } else { + return eoverflow(); + } +} + /** * Changes current position of zip file handle. * @@ -74,7 +57,7 @@ static int64_t __zipos_seek_impl(struct ZiposHandle *h, int64_t offset, */ int64_t __zipos_seek(struct ZiposHandle *h, int64_t offset, unsigned whence) { int64_t pos; - if ((pos = __zipos_seek_impl(h, offset, whence)) != -1) { + if ((pos = Seek(h, offset, whence)) != -1) { h->pos = pos; } return pos; diff --git a/libc/runtime/zipos.internal.h b/libc/runtime/zipos.internal.h index 59f8e8207..2e82e59e3 100644 --- a/libc/runtime/zipos.internal.h +++ b/libc/runtime/zipos.internal.h @@ -56,7 +56,7 @@ int64_t __zipos_seek(struct ZiposHandle *, int64_t, unsigned); int __zipos_fcntl(int, int, uintptr_t); int __zipos_notat(int, const char *); void *__zipos_mmap(void *, uint64_t, int32_t, int32_t, struct ZiposHandle *, - int64_t) dontasan; + int64_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/sock/kntwsadata.c b/libc/sock/kntwsadata.c index 982413643..660819021 100644 --- a/libc/sock/kntwsadata.c +++ b/libc/sock/kntwsadata.c @@ -49,6 +49,6 @@ textwindows dontasan void WinSockInit(void) { NTTRACE("WSAStartup()"); if ((rc = WSAStartup(VERSION, &kNtWsaData)) != 0 || kNtWsaData.wVersion != VERSION) { - ExitProcess(1 << 8); + _Exit(1); } } diff --git a/libc/sock/nointernet.c b/libc/sock/nointernet.c deleted file mode 100644 index 7af468530..000000000 --- a/libc/sock/nointernet.c +++ /dev/null @@ -1,341 +0,0 @@ -/*-*- 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/calls/struct/bpf.internal.h" -#include "libc/calls/struct/filter.internal.h" -#include "libc/calls/struct/seccomp.internal.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/syscall_support-sysv.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/describeflags.internal.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/likely.h" -#include "libc/macros.internal.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/msghdr.h" -#include "libc/sock/struct/sockaddr.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/audit.h" -#include "libc/sysv/consts/nr.h" -#include "libc/sysv/consts/nrlinux.h" -#include "libc/sysv/consts/pr.h" -#include "libc/sysv/consts/ptrace.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/errfuns.h" -#include "net/http/ip.h" -#ifdef __x86_64__ - -#define ORIG_RAX 120 -#define RAX 80 -#define RDI 112 -#define RSI 104 -#define RDX 96 -#define R8 72 -#define R9 64 -#define __WALL 0x40000000 - -#define OFF(f) offsetof(struct seccomp_data, f) - -#if 0 -#define DEBUG(...) kprintf(__VA_ARGS__) -#else -#define DEBUG(...) donothing -#endif - -#define ORDIE(x) \ - do { \ - if (UNLIKELY((x) == -1)) { \ - DEBUG("%s:%d: %s failed %m\n", __FILE__, __LINE__, #x); \ - notpossible; \ - } \ - } while (0) - -static const struct sock_filter kInetBpf[] = { - // cargo culted architecture assertion - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(arch)), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), - // block system calls from the future - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, OFF(nr)), - BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __NR_linux_memfd_secret, 0, 1), - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | 38), // ENOSYS - // only allow local and internet sockets - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_socket, 0, 5), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x001, 2, 0), // AF_UNIX - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x002, 1, 0), // AF_INET - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | 1), // EPERM - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), - // support for these not implemented yet - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x133, 0, 1), // sendmmsg - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | 1), // EPERM - // trace syscalls with struct sockaddr - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x02e, 3, 0), // sendmsg - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x02c, 2, 0), // sendto - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x031, 1, 0), // bind - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x02a, 0, 1), // connect - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE), - // default course of action - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), -}; - -static int PeekData(int pid, long addr, void *buf, size_t size) { - long i, j, w; - for (i = 0; i < size; i += sizeof(long)) { - if (sys_ptrace(PTRACE_PEEKTEXT, pid, addr + i, &w) != -1) { - for (j = 0; i + j < size && j < sizeof(long); ++j) { - ((char *)buf)[i + j] = w; - w >>= 8; - } - } else { - return -1; - } - } - return 0; -} - -static void LogProcessEvent(int main, int pid, int ws) { - DEBUG("trace: %s%06d%s 0x%06x", // - pid == main ? "\e[31;1m" : "", // - pid, // - pid == main ? "\e[0m" : "", // - ws); - if (WIFEXITED(ws)) { - DEBUG(" exit %d", WEXITSTATUS(ws)); - } - if (WIFSIGNALED(ws)) { - DEBUG(" sig %d", WTERMSIG(ws)); - } - if (WIFSTOPPED(ws)) { - DEBUG(" stop %s %s", strsignal(WSTOPSIG(ws)), - DescribePtraceEvent((ws & 0xff0000) >> 16)); - } - if (WIFCONTINUED(ws)) { - DEBUG(" cont"); - } - if (WCOREDUMP(ws)) { - DEBUG(" core"); - } - DEBUG("\n"); -} - -static int Raise(int sig) { - sigset_t mask; - sigaction(sig, &(struct sigaction){0}, 0); - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, 0); - kill(getpid(), sig); - sigdelset(&mask, sig); - sigprocmask(SIG_SETMASK, &mask, 0); - _Exit(128 + sig); -} - -static bool IsSockaddrAllowed(struct sockaddr_storage *addr) { - uint32_t ip; - if (addr->ss_family == AF_UNIX) { - return true; - } - if (addr->ss_family == AF_INET) { - ip = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); - if (IsPrivateIp(ip) || IsLoopbackIp(ip)) { - return true; - } else { - kprintf("warning: attempted to communicate with public ip " - "%hhd.%hhd.%hhd.%hhd\n", - ip >> 24, ip >> 16, ip >> 8, ip); - return false; - } - } - DEBUG("bad family %d\n", addr->ss_family); - return false; -} - -static void OnSockaddrSyscall(int pid, int r1, int r2) { - long si, dx; - uint32_t addrlen; - struct sockaddr_storage addr = {0}; - ORDIE(sys_ptrace(PTRACE_PEEKUSER, pid, r1, &si)); - ORDIE(sys_ptrace(PTRACE_PEEKUSER, pid, r2, &dx)); - addrlen = dx; - if (!si) { - // if address isn't supplied, it's probably safe. for example, - // send() is implemented in cosmo using sendto() with 0/0 addr - return; - } - if (PeekData(pid, si, &addr, MIN(addrlen, sizeof(addr))) == -1) { - DEBUG("failed to peek addr\n"); // probably an efault - goto Deny; - } - if (IsSockaddrAllowed(&addr)) { - return; - } else { - goto Deny; - } -Deny: - ORDIE(sys_ptrace(PTRACE_POKEUSER, pid, ORIG_RAX, -1)); -} - -static void OnSendmsg(int pid) { - long si; - struct msghdr msg = {0}; - struct sockaddr_storage addr = {0}; - ORDIE(sys_ptrace(PTRACE_PEEKUSER, pid, RSI, &si)); - if (PeekData(pid, si, &msg, sizeof(msg)) == -1) { - DEBUG("failed to peek msg\n"); // probably an efault - goto Deny; - } - if (!msg.msg_name) { - // if address isn't supplied, it's probably fine. - return; - } - if (PeekData(pid, (long)msg.msg_name, &addr, - MIN(msg.msg_namelen, sizeof(addr))) == -1) { - DEBUG("failed to peek msg name\n"); // probably an efault - goto Deny; - } - if (IsSockaddrAllowed(&addr)) { - return; - } else { - goto Deny; - } -Deny: - ORDIE(sys_ptrace(PTRACE_POKEUSER, pid, ORIG_RAX, -1)); -} - -static void HandleSeccompTrace(int pid) { - long ax; - ORDIE(sys_ptrace(PTRACE_PEEKUSER, pid, ORIG_RAX, &ax)); - switch (ax) { - case 0x031: // bind - case 0x02a: // connect - OnSockaddrSyscall(pid, RSI, RDX); - break; - case 0x02c: // sendto - OnSockaddrSyscall(pid, R8, R9); - break; - case 0x02e: // sendmsg - OnSendmsg(pid); - break; - default: - break; - } -} - -static int WaitForTrace(int main) { - int ws, pid; - for (;;) { - // waits for state change on any child process or thread - // eintr isn't possible since we're blocking all signals - ORDIE(pid = waitpid(-1, &ws, __WALL)); - LogProcessEvent(main, pid, ws); - if (WIFEXITED(ws)) { - if (pid == main) { - _Exit(WEXITSTATUS(ws)); - } - } else if (WIFSIGNALED(ws)) { - if (pid == main) { - Raise(WTERMSIG(ws)); - } - } else if (WIFSTOPPED(ws)) { - if ((ws >> 8) == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { - return pid; - } else if ((ws >> 8) == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) { - ORDIE(ptrace(PTRACE_CONT, pid, 0, 0)); - } else if ((ws >> 8) == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) || - (ws >> 8) == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) || - (ws >> 8) == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) { - ORDIE(ptrace(PTRACE_CONT, pid, 0, 0)); - } else { - ORDIE(ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(ws))); - } - } - } -} - -/** - * Disables internet access. - * - * Warning: This function uses ptrace to react to seccomp filter events. - * This approach is effective, but it's not bulletproof, since a highly - * motivated attacker could theoretically use threads to modify sockaddr - * in the short time between it being monitored and the actual syscall. - */ -int nointernet(void) { - int ws, act, main; - sigset_t set, old; - struct sock_fprog prog = {.filter = kInetBpf, .len = ARRAYLEN(kInetBpf)}; - - // seccomp bpf and ptrace are pretty much just linux for now. - if (!IsLinux() || !__is_linux_2_6_23()) { - return enosys(); - } - - // prevent crash handlers from intercepting sigsegv - ORDIE(sigfillset(&set)); - ORDIE(sigprocmask(SIG_SETMASK, &set, &old)); - - // create traced child that'll replace this program - if ((main = fork()) == -1) { - ORDIE(sigprocmask(SIG_SETMASK, &old, 0)); - return -1; - } - if (!main) { - if (sys_ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) { - // there can be only one - // throw sigsegv on eperm - // we're already being traced - asm("hlt"); - } - prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); - ORDIE(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)); - ORDIE(kill(getpid(), SIGSTOP)); - ORDIE(sigprocmask(SIG_SETMASK, &old, 0)); - // return to caller from child - return 0; - } - - // wait for child to stop itself - ORDIE(waitpid(main, &ws, 0)); - if (WIFSIGNALED(ws)) { - // child couldn't enable ptrace or seccomp - sigprocmask(SIG_SETMASK, &old, 0); - return eperm(); - } - npassert(WIFSTOPPED(ws)); - - // parent process becomes monitor of subprocess tree. all signals - // continue to be blocked since we assume they'll also be sent to - // children, which will die, and then the monitor dies afterwards - ORDIE(sys_ptrace(PTRACE_SETOPTIONS, main, 0, - PTRACE_O_TRACESECCOMP | PTRACE_O_TRACEFORK | - PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | - PTRACE_O_TRACEEXEC)); - for (act = main;;) { - ORDIE(sys_ptrace(PTRACE_CONT, act, 0, 0)); - act = WaitForTrace(main); - HandleSeccompTrace(act); - } -} - -#endif /* __x86_64__ */ diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 567b61770..06ea84ec4 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -40,19 +40,25 @@ #include "libc/sock/sendfile.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" // sendfile() isn't specified as raising eintr static textwindows int SendfileBlock(int64_t handle, struct NtOverlapped *overlapped) { + struct PosixThread *pt; uint32_t i, got, flags = 0; if (WSAGetLastError() != kNtErrorIoPending && WSAGetLastError() != WSAEINPROGRESS) { NTTRACE("TransmitFile failed %lm"); return __winsockerr(); } + pt = _pthread_self(); + pt->abort_errno = 0; + pt->ioverlap = overlapped; + pt->iohandle = handle; for (;;) { i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true, - __SIG_POLLING_INTERVAL_MS, true); + __SIG_IO_INTERVAL_MS, true); if (i == kNtWaitFailed) { NTTRACE("WSAWaitForMultipleEvents failed %lm"); return __winsockerr(); @@ -65,9 +71,14 @@ static textwindows int SendfileBlock(int64_t handle, break; } } + pt->ioverlap = 0; + pt->iohandle = 0; if (WSAGetOverlappedResult(handle, overlapped, &got, false, &flags)) { return got; } else { + if (WSAGetLastError() == kNtErrorOperationAborted) { + errno = pt->abort_errno; + } return -1; } } @@ -99,7 +110,7 @@ static dontinline textwindows ssize_t sys_sendfile_nt( return ebadf(); } struct NtOverlapped ov = { - .Pointer = (void *)(intptr_t)offset, + .Pointer = offset, .hEvent = WSACreateEvent(), }; if (TransmitFile(oh, ih, uptobytes, 0, &ov, 0, 0)) { diff --git a/libc/sock/sock.h b/libc/sock/sock.h index 88de46834..124b6d099 100644 --- a/libc/sock/sock.h +++ b/libc/sock/sock.h @@ -2,9 +2,6 @@ #define COSMOPOLITAN_LIBC_SOCK_SOCK_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § system api » berkeley sockets ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ #define INET_ADDRSTRLEN 22 #define IFHWADDRLEN 6 @@ -27,7 +24,6 @@ uint32_t inet_addr(const char *); int parseport(const char *); uint32_t *GetHostIps(void); -int nointernet(void); int socket(int, int, int); int listen(int, int); int shutdown(int, int); diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index df86e577f..361eb7369 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -20,6 +20,7 @@ #include "libc/calls/bo.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/struct/timespec.h" #include "libc/errno.h" #include "libc/nt/enum/wait.h" #include "libc/nt/enum/wsa.h" @@ -35,66 +36,91 @@ #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" -static textwindows void __wsablock_abort(int64_t handle, - struct NtOverlapped *overlapped) { - unassert(CancelIoEx(handle, overlapped) || - GetLastError() == kNtErrorNotFound); -} - -textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped, +textwindows int __wsablock(struct Fd *f, struct NtOverlapped *overlapped, uint32_t *flags, int sigops, uint32_t timeout) { + bool nonblock; + int e, rc, err; uint32_t i, got; - int rc, abort_errno; + uint32_t waitfor; + struct PosixThread *pt; + struct timespec now, remain, interval, deadline; + if (WSAGetLastError() != kNtErrorIoPending) { // our i/o operation never happened because it failed return __winsockerr(); } - BEGIN_BLOCKING_OPERATION; + // our i/o operation is in flight and it needs to block - abort_errno = EAGAIN; - if (fd->flags & O_NONBLOCK) { - __wsablock_abort(fd->handle, overlapped); + nonblock = !!(f->flags & O_NONBLOCK); + pt = _pthread_self(); + pt->abort_errno = EAGAIN; + interval = timespec_frommillis(__SIG_IO_INTERVAL_MS); + deadline = timeout + ? timespec_add(timespec_real(), timespec_frommillis(timeout)) + : timespec_max; + e = errno; +BlockingOperation: + if (!nonblock) { + pt->ioverlap = overlapped; + pt->iohandle = f->handle; + } + if (nonblock) { + CancelIoEx(f->handle, overlapped); } else if (_check_interrupts(sigops)) { Interrupted: - abort_errno = errno; // EINTR or ECANCELED - __wsablock_abort(fd->handle, overlapped); + pt->abort_errno = errno; // EINTR or ECANCELED + CancelIoEx(f->handle, overlapped); } else { for (;;) { - i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true, - __SIG_POLLING_INTERVAL_MS, true); - if (i == kNtWaitFailed || i == kNtWaitTimeout) { + now = timespec_real(); + if (timespec_cmp(now, deadline) >= 0) { + CancelIoEx(f->handle, overlapped); + nonblock = true; + break; + } + remain = timespec_sub(deadline, now); + if (timespec_cmp(remain, interval) >= 0) { + waitfor = __SIG_IO_INTERVAL_MS; + } else { + waitfor = timespec_tomillis(remain); + } + i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true, waitfor, true); + if (i == kNtWaitFailed) { + // Failure should be an impossible condition, but MSDN lists + // WSAENETDOWN and WSA_NOT_ENOUGH_MEMORY as possible errors. + pt->abort_errno = WSAGetLastError(); + CancelIoEx(f->handle, overlapped); + nonblock = true; + break; + } else if (i == kNtWaitTimeout) { if (_check_interrupts(sigops)) { goto Interrupted; } - if (i == kNtWaitFailed) { - // Failure should be an impossible condition, but MSDN lists - // WSAENETDOWN and WSA_NOT_ENOUGH_MEMORY as possible errors, - // which we're going to hope are ephemeral. - SleepEx(__SIG_POLLING_INTERVAL_MS, false); - } - if (timeout) { - if (timeout <= __SIG_POLLING_INTERVAL_MS) { - __wsablock_abort(fd->handle, overlapped); - break; - } - timeout -= __SIG_POLLING_INTERVAL_MS; - } + continue; } else { break; } } } + pt->ioverlap = 0; + pt->iohandle = 0; + // overlapped is allocated on stack by caller, so it's important that // we wait for win32 to acknowledge that it's done using that memory. - if (WSAGetOverlappedResult(fd->handle, overlapped, &got, true, flags)) { + if (WSAGetOverlappedResult(f->handle, overlapped, &got, nonblock, flags)) { rc = got; } else { rc = -1; - if (WSAGetLastError() == kNtErrorOperationAborted) { - errno = abort_errno; + err = WSAGetLastError(); + if (err == kNtErrorOperationAborted) { + errno = pt->abort_errno; + } else if (err == kNtErrorIoIncomplete) { + errno = e; + goto BlockingOperation; } } - END_BLOCKING_OPERATION; return rc; } diff --git a/libc/stdio/fleaks.c b/libc/stdio/fleaks.c index 5e55aa772..67244690a 100644 --- a/libc/stdio/fleaks.c +++ b/libc/stdio/fleaks.c @@ -24,12 +24,14 @@ #include "libc/str/str.h" #include "libc/sysv/consts/f.h" +#define MIN_CLANDESTINE_FD 100 // e.g. kprintf's dup'd handle + void CheckForFileLeaks(void) { char msg[512]; char *p = msg; char *pe = msg + 256; bool gotsome = false; - for (int fd = 3; fd < 200; ++fd) { + for (int fd = 3; fd < MIN_CLANDESTINE_FD; ++fd) { if (fcntl(fd, F_GETFL) != -1) { if (!gotsome) { p = stpcpy(p, program_invocation_short_name); @@ -46,7 +48,6 @@ void CheckForFileLeaks(void) { } if (gotsome) { char proc[64]; - char *p = proc; *p++ = '\n'; *p = 0; write(2, msg, p - msg); diff --git a/libc/stdio/fmt.c b/libc/stdio/fmt.c index 2e16cd388..7e2d91bf0 100644 --- a/libc/stdio/fmt.c +++ b/libc/stdio/fmt.c @@ -1028,6 +1028,14 @@ int __fmt(void *fn, void *arg, const char *format, va_list va) { flags |= FLAGS_PRECISION; prec = 1; goto FormatString; + } else if (flags & (FLAGS_QUOTE | FLAGS_REPR)) { + p = "'\\0'"; + flags &= ~(FLAGS_QUOTE | FLAGS_REPR | FLAGS_HASH); + goto FormatString; + } else if (flags & FLAGS_HASH) { + flags &= ~FLAGS_HASH; + p = " "; + goto FormatString; } else { __FMT_PUT('\0'); break; diff --git a/libc/stdio/nftw.c b/libc/stdio/nftw.c index 124015711..59ea2d31a 100644 --- a/libc/stdio/nftw.c +++ b/libc/stdio/nftw.c @@ -29,6 +29,7 @@ #include "libc/calls/struct/dirent.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" +#include "libc/runtime/stack.h" #include "libc/stdio/ftw.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" @@ -168,9 +169,13 @@ int nftw(const char *dirpath, int fd_limit, int flags) { +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wframe-larger-than=" + char pathbuf[PATH_MAXIMUS+1]; + CheckLargeStackAllocation(pathbuf, sizeof(pathbuf)); +#pragma GCC pop_options int r, cs; size_t l; - char pathbuf[PATH_MAXIMUS+1]; if (fd_limit <= 0) return 0; diff --git a/libc/stdio/posix_spawn_file_actions.c b/libc/stdio/posix_spawn_file_actions.c deleted file mode 100644 index 4f360391e..000000000 --- a/libc/stdio/posix_spawn_file_actions.c +++ /dev/null @@ -1,131 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/errno.h" -#include "libc/fmt/fmt.h" -#include "libc/mem/mem.h" -#include "libc/stdio/posix_spawn.h" -#include "libc/stdio/posix_spawn.internal.h" -#include "libc/str/str.h" - -static int AddFileAction(posix_spawn_file_actions_t *l, - struct _posix_faction a) { - struct _posix_faction *ap; - if (!(ap = malloc(sizeof(*ap)))) return ENOMEM; - *ap = a; - while (*l) l = &(*l)->next; - *l = ap; - return 0; -} - -/** - * Initializes posix_spawn() file actions list. - * - * File actions get applied in the same order as they're registered. - * - * @param file_actions will need posix_spawn_file_actions_destroy() - * @return 0 on success, or errno on error - */ -int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) { - *file_actions = 0; - return 0; -} - -/** - * Destroys posix_spawn() file actions list. - * - * This function is safe to call multiple times. - * - * @param file_actions was initialized by posix_spawn_file_actions_init() - * @return 0 on success, or errno on error - */ -int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) { - if (*file_actions) { - posix_spawn_file_actions_destroy(&(*file_actions)->next); - free((*file_actions)->path); - free(*file_actions); - *file_actions = 0; - } - return 0; -} - -/** - * Add a close action to object. - * - * @param file_actions was initialized by posix_spawn_file_actions_init() - * @return 0 on success, or errno on error - * @raise ENOMEM if we require more vespene gas - * @raise EBADF if `fildes` is negative - */ -int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, - int fildes) { - if (fildes < 0) return EBADF; - if (IsWindows() && fildes > 2) return 0; - return AddFileAction(file_actions, (struct _posix_faction){ - .action = _POSIX_SPAWN_CLOSE, - .fildes = fildes, - }); -} - -/** - * Add a dup2 action to object. - * - * @param file_actions was initialized by posix_spawn_file_actions_init() - * @return 0 on success, or errno on error - * @raise ENOMEM if we require more vespene gas - * @raise EBADF if 'fildes' or `newfildes` is negative - * @raise ENOTSUP if `newfildes` isn't 0, 1, or 2 on Windows - */ -int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, - int fildes, int newfildes) { - if (fildes < 0 || newfildes < 0) return EBADF; - if (IsWindows() && newfildes > 2) return ENOTSUP; - return AddFileAction(file_actions, (struct _posix_faction){ - .action = _POSIX_SPAWN_DUP2, - .fildes = fildes, - .newfildes = newfildes, - }); -} - -/** - * Add an open action to object. - * - * @param file_actions was initialized by posix_spawn_file_actions_init() - * @param fildes is what open() result gets duplicated to - * @param path will be safely copied - * @return 0 on success, or errno on error - * @raise ENOMEM if we require more vespene gas - * @raise EBADF if `fildes` is negative - * @raise ENOTSUP if `fildes` isn't 0, 1, or 2 on Windows - */ -int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions, - int fildes, const char *path, int oflag, - unsigned mode) { - char *path2; - if (fildes < 0) return EBADF; - if (IsWindows() && fildes > 2) return ENOTSUP; - if (!(path2 = strdup(path))) return ENOMEM; - return AddFileAction(file_actions, (struct _posix_faction){ - .action = _POSIX_SPAWN_OPEN, - .fildes = fildes, - .path = path2, - .oflag = oflag, - .mode = mode, - }); -} diff --git a/libc/stdio/posix_spawnattr.c b/libc/stdio/posix_spawnattr.c deleted file mode 100644 index 12a51240e..000000000 --- a/libc/stdio/posix_spawnattr.c +++ /dev/null @@ -1,291 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/calls/struct/sigset.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/stdio/posix_spawn.h" -#include "libc/stdio/posix_spawn.internal.h" -#include "libc/sysv/consts/sig.h" - -/** - * Initialize posix_spawn() attributes object with default values. - * - * @param attr needs to be passed to posix_spawnattr_destroy() later - * @return 0 on success, or errno on error - * @raise ENOMEM if we require more vespene gas - */ -int posix_spawnattr_init(posix_spawnattr_t *attr) { - int rc, e = errno; - struct _posix_spawna *a; - if ((a = calloc(1, sizeof(struct _posix_spawna)))) { - *attr = a; - rc = 0; - } else { - rc = errno; - errno = e; - } - return rc; -} - -/** - * Destroys posix_spawn() attributes object. - * - * This function is safe to call multiple times. - * - * @param attr was initialized by posix_spawnattr_init() - * @return 0 on success, or errno on error - */ -int posix_spawnattr_destroy(posix_spawnattr_t *attr) { - if (*attr) { - free(*attr); - *attr = 0; - } - return 0; -} - -/** - * Gets posix_spawn() flags. - * - * @param attr was initialized by posix_spawnattr_init() - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) { - *flags = (*attr)->flags; - return 0; -} - -/** - * Sets posix_spawn() flags. - * - * @param attr was initialized by posix_spawnattr_init() - * @param flags may have any of the following - * - `POSIX_SPAWN_RESETIDS` - * - `POSIX_SPAWN_SETPGROUP` - * - `POSIX_SPAWN_SETSIGDEF` - * - `POSIX_SPAWN_SETSIGMASK` - * - `POSIX_SPAWN_SETSCHEDPARAM` - * - `POSIX_SPAWN_SETSCHEDULER` - * - `POSIX_SPAWN_SETSID` - * @return 0 on success, or errno on error - * @raise EINVAL if `flags` has invalid bits - */ -int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { - if (flags & - ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | - POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM | - POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSID)) { - return EINVAL; - } - (*attr)->flags = flags; - return 0; -} - -/** - * Gets process group id associated with attributes. - * - * @param attr was initialized by posix_spawnattr_init() - * @param pgroup receives the result on success - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) { - *pgroup = (*attr)->pgroup; - return 0; -} - -/** - * Specifies process group into which child process is placed. - * - * Setting `pgroup` to zero will ensure newly created processes are - * placed within their own brand new process group. - * - * This setter also sets the `POSIX_SPAWN_SETPGROUP` flag. - * - * @param attr was initialized by posix_spawnattr_init() - * @param pgroup is the process group id, or 0 for self - * @return 0 on success, or errno on error - */ -int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) { - (*attr)->flags |= POSIX_SPAWN_SETPGROUP; - (*attr)->pgroup = pgroup; - return 0; -} - -/** - * Gets signal mask for sigprocmask() in child process. - * - * The signal mask is applied to the child process in such a way that - * signal handlers from the parent process can't get triggered in the - * child process. - * - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, - sigset_t *sigmask) { - *sigmask = (*attr)->sigmask; - return 0; -} - -/** - * Specifies signal mask for sigprocmask() in child process. - * - * This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag. - * - * @return 0 on success, or errno on error - */ -int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, - const sigset_t *sigmask) { - (*attr)->flags |= POSIX_SPAWN_SETSIGMASK; - (*attr)->sigmask = *sigmask; - return 0; -} - -/** - * Retrieves which signals will be restored to `SIG_DFL`. - * - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr, - sigset_t *sigdefault) { - *sigdefault = (*attr)->sigdefault; - return 0; -} - -/** - * Specifies which signals should be restored to `SIG_DFL`. - * - * This routine isn't necessary in most cases, since posix_spawn() by - * default will try to avoid vfork() race conditions by tracking what - * signals have a handler function and then resets them automatically - * within the child process, before applying the child's signal mask. - * This function may be used to ensure the `SIG_IGN` disposition will - * not propagate across execve in cases where this process explicitly - * set the signals to `SIG_IGN` earlier (since posix_spawn() will not - * issue O(128) system calls just to be totally pedantic about that). - * - * Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`. - * - * @return 0 on success, or errno on error - */ -int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, - const sigset_t *sigdefault) { - (*attr)->flags |= POSIX_SPAWN_SETSIGDEF; - (*attr)->sigdefault = *sigdefault; - return 0; -} - -/** - * Gets resource limit for spawned process. - * - * @return 0 on success, or errno on error - * @raise EINVAL if `resource` is invalid - * @raise ENOENT if `resource` is absent - */ -int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource, - struct rlimit *rlim) { - if ((0 <= resource && resource < ARRAYLEN((*attr)->rlim))) { - if (((*attr)->rlimset & (1u << resource))) { - *rlim = (*attr)->rlim[resource]; - return 0; - } else { - return ENOENT; - } - } else { - return EINVAL; - } -} - -/** - * Sets resource limit on spawned process. - * - * Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`. - * - * @return 0 on success, or errno on error - * @raise EINVAL if resource is invalid - */ -int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource, - const struct rlimit *rlim) { - if (0 <= resource && resource < ARRAYLEN((*attr)->rlim)) { - (*attr)->flags |= POSIX_SPAWN_SETRLIMIT; - (*attr)->rlimset |= 1u << resource; - (*attr)->rlim[resource] = *rlim; - return 0; - } else { - return EINVAL; - } -} - -/** - * Gets scheduler policy that'll be used for spawned process. - * - * @param attr was initialized by posix_spawnattr_init() - * @param schedpolicy receives the result - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, - int *schedpolicy) { - *schedpolicy = (*attr)->schedpolicy; - return 0; -} - -/** - * Specifies scheduler policy override for spawned process. - * - * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`. - * - * @param attr was initialized by posix_spawnattr_init() - * @return 0 on success, or errno on error - */ -int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) { - (*attr)->flags |= POSIX_SPAWN_SETSCHEDULER; - (*attr)->schedpolicy = schedpolicy; - return 0; -} - -/** - * Gets scheduler parameter. - * - * @param attr was initialized by posix_spawnattr_init() - * @param schedparam receives the result - * @return 0 on success, or errno on error - */ -int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr, - struct sched_param *schedparam) { - *schedparam = (*attr)->schedparam; - return 0; -} - -/** - * Specifies scheduler parameter override for spawned process. - * - * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`. - * - * @param attr was initialized by posix_spawnattr_init() - * @param schedparam receives the result - * @return 0 on success, or errno on error - */ -int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, - const struct sched_param *schedparam) { - (*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM; - (*attr)->schedparam = *schedparam; - return 0; -} diff --git a/libc/stdio/stdio.mk b/libc/stdio/stdio.mk index 7e9e08069..53037f7f5 100644 --- a/libc/stdio/stdio.mk +++ b/libc/stdio/stdio.mk @@ -32,6 +32,7 @@ LIBC_STDIO_A_DIRECTDEPS = \ LIBC_NT_ADVAPI32 \ LIBC_NT_KERNEL32 \ LIBC_RUNTIME \ + LIBC_PROC \ LIBC_STR \ LIBC_SYSV \ LIBC_SYSV_CALLS \ @@ -48,6 +49,9 @@ $(LIBC_STDIO_A).pkg: \ $(LIBC_STDIO_A_OBJS) \ $(foreach x,$(LIBC_STDIO_A_DIRECTDEPS),$($(x)_A).pkg) +# offer assurances about the stack safety of cosmo libc +$(LIBC_STDIO_A_OBJS): private COPTS += -Wframe-larger-than=4096 -Walloca-larger-than=4096 + o/$(MODE)/libc/stdio/fputc.o: private \ CFLAGS += \ -O3 @@ -57,8 +61,6 @@ o//libc/stdio/appendw.o: private \ -Os o/$(MODE)/libc/stdio/dirstream.o \ -o/$(MODE)/libc/stdio/posix_spawnattr.o \ -o/$(MODE)/libc/stdio/posix_spawn_file_actions.o \ o/$(MODE)/libc/stdio/mt19937.o: private \ CFLAGS += \ -ffunction-sections diff --git a/libc/stdio/tmpfile.c b/libc/stdio/tmpfile.c index ab110bbb2..6211f1cc8 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/stdio/tmpfile.c @@ -33,8 +33,7 @@ * * This creates a secure temporary file inside $TMPDIR. If it isn't * defined, then /tmp is used on UNIX and GetTempPath() is used on the - * New Technology. This resolution of $TMPDIR happens once in a ctor, - * which is copied to the `kTmpPath` global. + * New Technology. This resolution of $TMPDIR happens once in a ctor. * * Once fclose() is called, the returned file is guaranteed to be * deleted automatically. On UNIX the file is unlink()'d before this diff --git a/libc/stdio/vcscanf.c b/libc/stdio/vcscanf.c index 5669b47e8..80373886b 100644 --- a/libc/stdio/vcscanf.c +++ b/libc/stdio/vcscanf.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" -#include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -303,9 +302,9 @@ int __vcscanf(int callback(void *), // if (discard) { buf = NULL; } else if (ismalloc) { - buf = _weaken(malloc)(bufsize * charbytes); + buf = malloc(bufsize * charbytes); struct FreeMe *entry; - if (buf && (entry = _weaken(calloc)(1, sizeof(struct FreeMe)))) { + if (buf && (entry = calloc(1, sizeof(struct FreeMe)))) { entry->ptr = buf; entry->next = freeme; freeme = entry; @@ -317,7 +316,7 @@ int __vcscanf(int callback(void *), // size_t j = 0; for (;;) { if (ismalloc && !width && j + 2 + 1 >= bufsize && - !_weaken(__grow)(&buf, &bufsize, charbytes, 0)) { + !__grow(&buf, &bufsize, charbytes, 0)) { width = bufsize - 1; } if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) { @@ -372,11 +371,11 @@ int __vcscanf(int callback(void *), // } } Done: - while (freeme && _weaken(free)) { + while (freeme) { struct FreeMe *entry = freeme; freeme = entry->next; - if (items == -1) _weaken(free)(entry->ptr); - _weaken(free)(entry); + if (items == -1) free(entry->ptr); + free(entry); } return items; } diff --git a/libc/str/isutf8.c b/libc/str/isutf8.c index 25d5ceb55..84361ab1c 100644 --- a/libc/str/isutf8.c +++ b/libc/str/isutf8.c @@ -49,7 +49,7 @@ static const char kUtf8Dispatch[] = { * * @param size if -1 implies strlen */ -dontasan bool isutf8(const void *data, size_t size) { +bool isutf8(const void *data, size_t size) { long c; const char *p, *e; if (size == -1) size = data ? strlen(data) : 0; diff --git a/libc/str/memmem.c b/libc/str/memmem.c index 0f3c49981..b4dc164c1 100644 --- a/libc/str/memmem.c +++ b/libc/str/memmem.c @@ -32,7 +32,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); * @param needlelen is its character count * @return pointer to first result or NULL if not found */ -dontasan void *memmem(const void *haystack, size_t haystacklen, +void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { #if defined(__x86_64__) && !defined(__chibicc__) char c; diff --git a/libc/str/memrchr16.c b/libc/str/memrchr16.c index 4ee51084b..edd452a7a 100644 --- a/libc/str/memrchr16.c +++ b/libc/str/memrchr16.c @@ -36,7 +36,7 @@ static inline const char16_t *memrchr16_pure(const char16_t *s, char16_t c, } #if defined(__x86_64__) && !defined(__chibicc__) -dontasan static inline const char16_t *memrchr16_sse(const char16_t *s, +static inline const char16_t *memrchr16_sse(const char16_t *s, char16_t c, size_t n) { size_t i; unsigned m; diff --git a/libc/str/rawmemchr.c b/libc/str/rawmemchr.c index 6b1a45a2c..57f35946c 100644 --- a/libc/str/rawmemchr.c +++ b/libc/str/rawmemchr.c @@ -33,7 +33,7 @@ static inline const unsigned char *rawmemchr_pure(const unsigned char *s, #if defined(__x86_64__) && !defined(__chibicc__) typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); -dontasan static inline const char *rawmemchr_sse(const char *s, +static inline const char *rawmemchr_sse(const char *s, unsigned char c) { unsigned k; unsigned m; @@ -54,7 +54,7 @@ dontasan static inline const char *rawmemchr_sse(const char *s, } #endif -static inline dontasan uint64_t UncheckedAlignedRead64(const unsigned char *p) { +static inline uint64_t UncheckedAlignedRead64(const unsigned char *p) { return (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 | (uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 | (uint64_t)p[1] << 010 | (uint64_t)p[0] << 000; diff --git a/libc/str/str.mk b/libc/str/str.mk index 65aee1c11..da938328b 100644 --- a/libc/str/str.mk +++ b/libc/str/str.mk @@ -86,19 +86,11 @@ o/$(MODE)/libc/str/windowstimetotimespec.o: private \ CFLAGS += \ -O2 -# we can't use compiler magic because: -# kprintf() depends on these functions -o/$(MODE)/libc/fmt/strsignal.greg.o: private \ - CFLAGS += \ - -fpie \ - -ffreestanding \ - $(NO_MAGIC) - -# we can't use sanitizers because: -# WinMain calls this -o/$(MODE)/libc/str/tprecode8to16.o: private \ +$(LIBC_STR_A_OBJS): private \ COPTS += \ - -fno-sanitize=all + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 o/$(MODE)/libc/str/eastasianwidth.bin: \ libc/str/eastasianwidth.txt \ diff --git a/libc/str/strcasecmp.c b/libc/str/strcasecmp.c index c10827062..d1f42cf70 100644 --- a/libc/str/strcasecmp.c +++ b/libc/str/strcasecmp.c @@ -29,7 +29,7 @@ * @return is <0, 0, or >0 based on tolower(uint8_t) comparison * @asyncsignalsafe */ -dontasan int strcasecmp(const char *a, const char *b) { +int strcasecmp(const char *a, const char *b) { int x, y; size_t i = 0; uint64_t v, w; diff --git a/libc/str/strcasestr.c b/libc/str/strcasestr.c index 27e8205d7..1188db69e 100644 --- a/libc/str/strcasestr.c +++ b/libc/str/strcasestr.c @@ -35,7 +35,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); * @asyncsignalsafe * @see strstr() */ -dontasan char *strcasestr(const char *haystack, const char *needle) { +char *strcasestr(const char *haystack, const char *needle) { #if defined(__x86_64__) && !defined(__chibicc__) char c; size_t i; diff --git a/libc/str/strlcat.c b/libc/str/strlcat.c index 6b9e02d70..c4a285c7a 100644 --- a/libc/str/strlcat.c +++ b/libc/str/strlcat.c @@ -33,6 +33,9 @@ asm(".include \"libc/disclaimer.inc\""); * characters will be copied. Always NUL terminates (unless `dsize <= * strlen(dst)`). Returns `strlen(src) + MIN(dsize, strlen(initial * dst))`. If `retval >= dsize`, truncation occurred. + * + * @asyncsignalsafe + * @vforksafe */ size_t strlcat(char *dst, const char *src, size_t dsize) diff --git a/libc/str/strlen16.c b/libc/str/strlen16.c index 8acc5ad5d..866ee2942 100644 --- a/libc/str/strlen16.c +++ b/libc/str/strlen16.c @@ -29,7 +29,7 @@ typedef char16_t xmm_t __attribute__((__vector_size__(16), __aligned__(16))); * @return number of shorts (excluding NUL) * @asyncsignalsafe */ -dontasan size_t strlen16(const char16_t *s) { +size_t strlen16(const char16_t *s) { #if defined(__x86_64__) && !defined(__chibicc__) size_t n; xmm_t z = {0}; diff --git a/libc/str/strnlen16.c b/libc/str/strnlen16.c index 19a2b4e33..da55be7ac 100644 --- a/libc/str/strnlen16.c +++ b/libc/str/strnlen16.c @@ -27,7 +27,7 @@ * @return number of shorts * @asyncsignalsafe */ -dontasan size_t strnlen16(const char16_t *s, size_t n) { +size_t strnlen16(const char16_t *s, size_t n) { size_t i; for (i = 0;; ++i) { if (i == n || !s[i]) break; diff --git a/libc/str/strnlen_s.c b/libc/str/strnlen_s.c index 92c5ed5a3..410539640 100644 --- a/libc/str/strnlen_s.c +++ b/libc/str/strnlen_s.c @@ -21,7 +21,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/str/str.h" -static dontasan size_t strnlen_s_x64(const char *s, size_t n, size_t i) { +static size_t strnlen_s_x64(const char *s, size_t n, size_t i) { uint64_t w; for (; i + 8 < n; i += 8) { w = *(uint64_t *)(s + i); @@ -45,7 +45,7 @@ static dontasan size_t strnlen_s_x64(const char *s, size_t n, size_t i) { * @return byte length * @asyncsignalsafe */ -dontasan size_t strnlen_s(const char *s, size_t n) { +size_t strnlen_s(const char *s, size_t n) { size_t i; if (!s) return 0; if (IsAsan()) __asan_verify(s, n); diff --git a/libc/str/strstr.c b/libc/str/strstr.c index 0d25edeab..3b64a1795 100644 --- a/libc/str/strstr.c +++ b/libc/str/strstr.c @@ -35,7 +35,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); * @see strcasestr() * @see memmem() */ -dontasan char *strstr(const char *haystack, const char *needle) { +char *strstr(const char *haystack, const char *needle) { #if defined(__x86_64__) && !defined(__chibicc__) size_t i; unsigned k, m; diff --git a/libc/str/tprecode16to8.c b/libc/str/tprecode16to8.c index d2eca2814..dffe760d8 100644 --- a/libc/str/tprecode16to8.c +++ b/libc/str/tprecode16to8.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/intrin/packsswb.h" #include "libc/intrin/pandn.h" @@ -27,8 +28,8 @@ static const int16_t kDel16[8] = {127, 127, 127, 127, 127, 127, 127, 127}; /* 10x speedup for ascii */ -static dontasan axdx_t tprecode16to8_sse2(char *dst, size_t dstsize, - const char16_t *src, axdx_t r) { +static axdx_t tprecode16to8_sse2(char *dst, size_t dstsize, const char16_t *src, + axdx_t r) { int16_t v1[8], v2[8], v3[8], vz[8]; memset(vz, 0, 16); while (r.ax + 8 < dstsize) { @@ -64,9 +65,11 @@ axdx_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) { r.ax = 0; r.dx = 0; for (;;) { - if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { +#if defined(__x86_64__) && !IsModeDbg() && !IsTiny() + if (!((uintptr_t)(src + r.dx) & 15)) { r = tprecode16to8_sse2(dst, dstsize, src, r); } +#endif if (!(x = src[r.dx++])) break; if (IsUtf16Cont(x)) continue; if (!IsUcs2(x)) { diff --git a/libc/str/wcslen.c b/libc/str/wcslen.c index 57aa11e40..6263d399d 100644 --- a/libc/str/wcslen.c +++ b/libc/str/wcslen.c @@ -29,7 +29,7 @@ typedef wchar_t xmm_t __attribute__((__vector_size__(16), __aligned__(16))); * @return number of wide characters (excluding NUL) * @asyncsignalsafe */ -dontasan size_t wcslen(const wchar_t *s) { +size_t wcslen(const wchar_t *s) { #if defined(__x86_64__) && !defined(__chibicc__) size_t n; xmm_t z = {0}; diff --git a/libc/str/wmemrchr.c b/libc/str/wmemrchr.c index 15280c0ea..d111a54e8 100644 --- a/libc/str/wmemrchr.c +++ b/libc/str/wmemrchr.c @@ -37,7 +37,7 @@ static inline const wchar_t *wmemrchr_pure(const wchar_t *s, wchar_t c, } #if defined(__x86_64__) && !defined(__chibicc__) -dontasan static inline const wchar_t *wmemrchr_sse(const wchar_t *s, wchar_t c, +static inline const wchar_t *wmemrchr_sse(const wchar_t *s, wchar_t c, size_t n) { size_t i; unsigned m; diff --git a/libc/sysv/calls/__sys_accept.S b/libc/sysv/calls/__sys_accept.S index 5dcc819bb..3f93c55aa 100644 --- a/libc/sysv/calls/__sys_accept.S +++ b/libc/sysv/calls/__sys_accept.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_accept,0x81e81ea1d281e82b,2250,30,globl,hidden +.scall __sys_accept,0x81e81ea1d281e82b,2250,2078,globl,hidden diff --git a/libc/sysv/calls/__sys_connect.S b/libc/sysv/calls/__sys_connect.S index 1a15cc582..dad48c2e2 100644 --- a/libc/sysv/calls/__sys_connect.S +++ b/libc/sysv/calls/__sys_connect.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_connect,0x862862862286282a,2251,98,globl,hidden +.scall __sys_connect,0x862862862286282a,2251,2146,globl,hidden diff --git a/libc/sysv/calls/__sys_fcntl_cp.S b/libc/sysv/calls/__sys_fcntl_cp.S index 9f40c679f..9c87a8b27 100644 --- a/libc/sysv/calls/__sys_fcntl_cp.S +++ b/libc/sysv/calls/__sys_fcntl_cp.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_fcntl_cp,0x85c85c85c285c848,2073,92,globl,hidden +.scall __sys_fcntl_cp,0x85c85c85c285c848,2073,2140,globl,hidden diff --git a/libc/sysv/calls/__sys_openat.S b/libc/sysv/calls/__sys_openat.S index 7ea349977..d11acb115 100644 --- a/libc/sysv/calls/__sys_openat.S +++ b/libc/sysv/calls/__sys_openat.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_openat,0x9d49419f329cf901,2104,463,globl,hidden +.scall __sys_openat,0x9d49419f329cf901,2104,2511,globl,hidden diff --git a/libc/sysv/calls/__sys_poll.S b/libc/sysv/calls/__sys_poll.S index b566b52cd..017f7f86a 100644 --- a/libc/sysv/calls/__sys_poll.S +++ b/libc/sysv/calls/__sys_poll.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_poll,0x8d18fc8d128e6807,4095,230,globl,hidden +.scall __sys_poll,0x8d18fc8d128e6807,4095,2278,globl,hidden diff --git a/libc/sysv/calls/__sys_wait4.S b/libc/sysv/calls/__sys_wait4.S index 8ae56c856..33cade9b3 100644 --- a/libc/sysv/calls/__sys_wait4.S +++ b/libc/sysv/calls/__sys_wait4.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall __sys_wait4,0x9c180b807280783d,2308,7,globl,hidden +.scall __sys_wait4,0x9c180b807280783d,2308,2055,globl,hidden diff --git a/libc/sysv/calls/sys_fdatasync.S b/libc/sysv/calls/sys_fdatasync.S index baa67bf9f..3af75d7fd 100644 --- a/libc/sysv/calls/sys_fdatasync.S +++ b/libc/sysv/calls/sys_fdatasync.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_fdatasync,0x8f185fa2628bb84b,2131,187,globl,hidden +.scall sys_fdatasync,0x8f185fa2628bb84b,2131,2235,globl,hidden diff --git a/libc/sysv/calls/sys_flock.S b/libc/sysv/calls/sys_flock.S index e71251303..759ce1973 100644 --- a/libc/sysv/calls/sys_flock.S +++ b/libc/sysv/calls/sys_flock.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_flock,0x8838838832883849,2080,131,globl,hidden +.scall sys_flock,0x8838838832883849,2080,2179,globl,hidden diff --git a/libc/sysv/calls/sys_fstatfs.S b/libc/sysv/calls/sys_fstatfs.S index 5ed6773e7..d009b257b 100644 --- a/libc/sysv/calls/sys_fstatfs.S +++ b/libc/sysv/calls/sys_fstatfs.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_fstatfs,0x89e840a2c295a88a,2092,346,globl,hidden +.scall sys_fstatfs,0x89e840a2c295a88a,2092,2394,globl,hidden diff --git a/libc/sysv/calls/sys_fsync.S b/libc/sysv/calls/sys_fsync.S index a56a2dee9..c12685e57 100644 --- a/libc/sysv/calls/sys_fsync.S +++ b/libc/sysv/calls/sys_fsync.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_fsync,0x85f85f85f285f84a,2130,95,globl,hidden +.scall sys_fsync,0x85f85f85f285f84a,2130,2143,globl,hidden diff --git a/libc/sysv/calls/sys_ftruncate.S b/libc/sysv/calls/sys_ftruncate.S index 0c4e53bce..18d74d3e4 100644 --- a/libc/sysv/calls/sys_ftruncate.S +++ b/libc/sysv/calls/sys_ftruncate.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_ftruncate,0x8c98a89e028c984d,2094,201,globl,hidden +.scall sys_ftruncate,0x8c98a89e028c984d,2094,2249,globl,hidden diff --git a/libc/sysv/calls/sys_ioctl_cp.S b/libc/sysv/calls/sys_ioctl_cp.S index 4b669a395..1100f504f 100644 --- a/libc/sysv/calls/sys_ioctl_cp.S +++ b/libc/sysv/calls/sys_ioctl_cp.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_ioctl_cp,0x8368368362836810,2077,54,globl,hidden +.scall sys_ioctl_cp,0x8368368362836810,2077,2102,globl,hidden diff --git a/libc/sysv/calls/sys_msgrcv.S b/libc/sysv/calls/sys_msgrcv.S index a45439189..6a8446bcd 100644 --- a/libc/sysv/calls/sys_msgrcv.S +++ b/libc/sysv/calls/sys_msgrcv.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_msgrcv,0x8e38e38e32905846,2236,261,globl +.scall sys_msgrcv,0x8e38e38e32905846,2236,2309,globl diff --git a/libc/sysv/calls/sys_msgsnd.S b/libc/sysv/calls/sys_msgsnd.S index 47d3d14d7..4f64cb650 100644 --- a/libc/sysv/calls/sys_msgsnd.S +++ b/libc/sysv/calls/sys_msgsnd.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_msgsnd,0x8e28e28e22904845,2237,260,globl +.scall sys_msgsnd,0x8e28e28e22904845,2237,2308,globl diff --git a/libc/sysv/calls/sys_msync.S b/libc/sysv/calls/sys_msync.S index 176825271..23b75094f 100644 --- a/libc/sysv/calls/sys_msync.S +++ b/libc/sysv/calls/sys_msync.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_msync,0x915900841284181a,2275,65,globl,hidden +.scall sys_msync,0x915900841284181a,2275,2113,globl,hidden diff --git a/libc/sysv/calls/sys_pread.S b/libc/sysv/calls/sys_pread.S index 5fbd8e7a8..9106a4ee0 100644 --- a/libc/sysv/calls/sys_pread.S +++ b/libc/sysv/calls/sys_pread.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_pread,0x8ad8a99db2899811,2115,153,globl,hidden +.scall sys_pread,0x8ad8a99db2899811,2115,2201,globl,hidden diff --git a/libc/sysv/calls/sys_preadv.S b/libc/sysv/calls/sys_preadv.S index 520b7863c..6c0712baf 100644 --- a/libc/sysv/calls/sys_preadv.S +++ b/libc/sysv/calls/sys_preadv.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_preadv,0x9218ab9212a1c927,2117,540,globl,hidden +.scall sys_preadv,0x9218ab9212a1c927,2117,2588,globl,hidden diff --git a/libc/sysv/calls/sys_pselect.S b/libc/sysv/calls/sys_pselect.S index efdbd8746..1a44a9c1b 100644 --- a/libc/sysv/calls/sys_pselect.S +++ b/libc/sysv/calls/sys_pselect.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_pselect,0x9b486ea0a298a90e,2120,394,globl,hidden +.scall sys_pselect,0x9b486ea0a298a90e,2120,2442,globl,hidden diff --git a/libc/sysv/calls/sys_pwrite.S b/libc/sysv/calls/sys_pwrite.S index d6c9faaeb..335d22462 100644 --- a/libc/sysv/calls/sys_pwrite.S +++ b/libc/sysv/calls/sys_pwrite.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_pwrite,0x8ae8aa9dc289a812,2116,154,globl,hidden +.scall sys_pwrite,0x8ae8aa9dc289a812,2116,2202,globl,hidden diff --git a/libc/sysv/calls/sys_pwritev.S b/libc/sysv/calls/sys_pwritev.S index 20bad99e3..dde71cd61 100644 --- a/libc/sysv/calls/sys_pwritev.S +++ b/libc/sysv/calls/sys_pwritev.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_pwritev,0x9228ac9222a1d928,2118,541,globl,hidden +.scall sys_pwritev,0x9228ac9222a1d928,2118,2589,globl,hidden diff --git a/libc/sysv/calls/sys_read.S b/libc/sysv/calls/sys_read.S index f2b2c33af..cd58c55eb 100644 --- a/libc/sysv/calls/sys_read.S +++ b/libc/sysv/calls/sys_read.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_read,0x8038038032803800,2111,3,globl,hidden +.scall sys_read,0x8038038032803800,2111,2051,globl,hidden diff --git a/libc/sysv/calls/sys_readv.S b/libc/sysv/calls/sys_readv.S index b046861c9..ec3ca292f 100644 --- a/libc/sysv/calls/sys_readv.S +++ b/libc/sysv/calls/sys_readv.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_readv,0x8788788782878813,2113,120,globl,hidden +.scall sys_readv,0x8788788782878813,2113,2168,globl,hidden diff --git a/libc/sysv/calls/sys_recvfrom.S b/libc/sysv/calls/sys_recvfrom.S index 018780247..86dace8db 100644 --- a/libc/sysv/calls/sys_recvfrom.S +++ b/libc/sysv/calls/sys_recvfrom.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_recvfrom,0x81d81d81d281d82d,2255,29,globl,hidden +.scall sys_recvfrom,0x81d81d81d281d82d,2255,2077,globl,hidden diff --git a/libc/sysv/calls/sys_recvmsg.S b/libc/sysv/calls/sys_recvmsg.S index e5e7cb0ab..f375c1c7c 100644 --- a/libc/sysv/calls/sys_recvmsg.S +++ b/libc/sysv/calls/sys_recvmsg.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_recvmsg,0x81b81b81b281b82f,2260,27,globl,hidden +.scall sys_recvmsg,0x81b81b81b281b82f,2260,2075,globl,hidden diff --git a/libc/sysv/calls/sys_select.S b/libc/sysv/calls/sys_select.S index a1694c75b..076535cfb 100644 --- a/libc/sysv/calls/sys_select.S +++ b/libc/sysv/calls/sys_select.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_select,0x9a184785d285d817,4095,93,globl,hidden +.scall sys_select,0x9a184785d285d817,4095,2141,globl,hidden diff --git a/libc/sysv/calls/sys_sem_wait.S b/libc/sysv/calls/sys_sem_wait.S index da413240f..94ed8bee2 100644 --- a/libc/sysv/calls/sys_sem_wait.S +++ b/libc/sysv/calls/sys_sem_wait.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_sem_wait,0x8fcfff992290ffff,4095,271,globl +.scall sys_sem_wait,0x8fcfff992290ffff,4095,2319,globl diff --git a/libc/sysv/calls/sys_sendmsg.S b/libc/sysv/calls/sys_sendmsg.S index c3974195a..5e6eb77c6 100644 --- a/libc/sysv/calls/sys_sendmsg.S +++ b/libc/sysv/calls/sys_sendmsg.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_sendmsg,0x81c81c81c281c82e,2259,28,globl,hidden +.scall sys_sendmsg,0x81c81c81c281c82e,2259,2076,globl,hidden diff --git a/libc/sysv/calls/sys_sendto.S b/libc/sysv/calls/sys_sendto.S index 11b36eb49..0a805a35c 100644 --- a/libc/sysv/calls/sys_sendto.S +++ b/libc/sysv/calls/sys_sendto.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_sendto,0x885885885288582c,2254,133,globl,hidden +.scall sys_sendto,0x885885885288582c,2254,2181,globl,hidden diff --git a/libc/sysv/calls/sys_sigsuspend.S b/libc/sysv/calls/sys_sigsuspend.S index 9465007a0..3a1f5ffc0 100644 --- a/libc/sysv/calls/sys_sigsuspend.S +++ b/libc/sysv/calls/sys_sigsuspend.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_sigsuspend,0x92686f955286f882,2181,111,globl,hidden +.scall sys_sigsuspend,0x92686f955286f882,2181,2159,globl,hidden diff --git a/libc/sysv/calls/sys_statfs.S b/libc/sysv/calls/sys_statfs.S index 7473757d5..bd69def3a 100644 --- a/libc/sysv/calls/sys_statfs.S +++ b/libc/sysv/calls/sys_statfs.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_statfs,0x89d83fa2b2959889,2091,345,globl,hidden +.scall sys_statfs,0x89d83fa2b2959889,2091,2393,globl,hidden diff --git a/libc/sysv/calls/sys_truncate.S b/libc/sysv/calls/sys_truncate.S index c8c6a557e..98d08b147 100644 --- a/libc/sysv/calls/sys_truncate.S +++ b/libc/sysv/calls/sys_truncate.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_truncate,0x8c88a79df28c884c,2093,200,globl,hidden +.scall sys_truncate,0x8c88a79df28c884c,2093,2248,globl,hidden diff --git a/libc/sysv/calls/sys_waitid.S b/libc/sysv/calls/sys_waitid.S index 83a65b23e..1a77e251d 100644 --- a/libc/sysv/calls/sys_waitid.S +++ b/libc/sysv/calls/sys_waitid.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_waitid,0xfffffffff28ad8f7,95,173,globl +.scall sys_waitid,0xfffffffff28ad8f7,95,2221,globl diff --git a/libc/sysv/calls/sys_write.S b/libc/sysv/calls/sys_write.S index 74c681ce2..35b8d40cf 100644 --- a/libc/sysv/calls/sys_write.S +++ b/libc/sysv/calls/sys_write.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_write,0x8048048042804801,2112,4,globl,hidden +.scall sys_write,0x8048048042804801,2112,2052,globl,hidden diff --git a/libc/sysv/calls/sys_writev.S b/libc/sysv/calls/sys_writev.S index 49a5df2cf..80af851d1 100644 --- a/libc/sysv/calls/sys_writev.S +++ b/libc/sysv/calls/sys_writev.S @@ -1,2 +1,2 @@ #include "libc/sysv/macros.internal.h" -.scall sys_writev,0x8798798792879814,2114,121,globl,hidden +.scall sys_writev,0x8798798792879814,2114,2169,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index aa08535b1..342071111 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -18,6 +18,7 @@ ╚────────────────────────────────────────────────────────────────'>/dev/null #*/ dir=libc/sysv/consts . libc/sysv/gen.sh +#include "libc/sysv/consts/ss.h" # The Fifth Bell System, Community Edition # » catalogue of carnage @@ -1057,6 +1058,8 @@ syscon limits _ARG_MAX 128*1024 128*1024 1024*1024 1024*1024 512*1024 51 syscon limits _NAME_MAX 255 255 255 255 255 255 511 255 # probably higher on windows? syscon limits _PATH_MAX 4096 4096 1024 1024 1024 1024 1024 260 # syscon limits _NSIG 64 64 32 32 128 32 64 64 # _SIG_MAXSIG on FreeBSD +syscon limits _MINSIGSTKSZ 2048 2048 32768 32768 4096 12288 8192 2048 # +syscon limits _SIGSTKSZ 8192 2048 131072 131072 36864 28672 40960 8192 # # unmount() flags # a.k.a. umount2() on linux @@ -1549,8 +1552,8 @@ syscon vid KDFONTOP 0x4b72 0x4b72 0 0 0 0 0 0 syscon nr __NR_exit 0x003c 0x005d 0x2000169 0x0169 0x01af 0x012e 0x136 0xfff # __bsdthread_terminate() on XNU, thr_exit() on FreeBSD, __threxit() on OpenBSD, __lwp_exit() on NetBSD syscon nr __NR_exit_group 0x00e7 0x005e 0x2000001 0x0001 0x0001 0x0001 0x001 0xfff syscon nr __NR_read 0x0000 0x003f 0x2000003 0x0003 0x0003 0x0003 0x003 0xfff -syscon nr __NR_write 0x0001 0x0040 0x2000004 0x0004 0x0004 0x0004 0x004 0xfff -syscon nr __NR_open 0x0002 0x00b4 0x2000005 0x0005 0x0005 0x0005 0x005 0xfff +syscon nr __NR_write 0x0001 0x0040 0x200018d 0x0004 0x0004 0x0004 0x004 0xfff # write_nocancel() on XNU +syscon nr __NR_open 0x0002 0x00b4 0x200018e 0x0005 0x0005 0x0005 0x005 0xfff # open_nocancel() on XNU syscon nr __NR_close 0x0003 0x0039 0x2000006 0x0006 0x0006 0x0006 0x006 0xfff syscon nr __NR_stat 0x0004 0x004f 0x2000152 0x0152 0xfff 0x0026 0x1b7 0xfff syscon nr __NR_fstat 0x0005 0x0050 0x2000153 0x0153 0x0227 0x0035 0x1b8 0xfff @@ -1574,7 +1577,7 @@ syscon nr __NR_pipe 0x0016 0x0fff 0x200002a 0x002a 0x021e 0x0107 0 syscon nr __NR_select 0x0017 0x0fff 0x200005d 0x005d 0x005d 0x0047 0x1a1 0xfff syscon nr __NR_pselect 0xfff 0x0fff 0x200018a 0x018a 0x020a 0x006e 0x1b4 0xfff syscon nr __NR_pselect6 0x010e 0x0048 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff -syscon nr __NR_sched_yield 0x0018 0x007c 0x200005d 0x005d 0x014b 0x012a 0x15e 0xfff # select() on XNU (previously swtch() but removed in 12.4) +syscon nr __NR_sched_yield 0x0018 0x007c 0x2000197 0x005d 0x014b 0x012a 0x15e 0xfff # select_nocancel() on XNU (previously swtch() but removed in 12.4) syscon nr __NR_mremap 0x0019 0x00d8 0xfff 0xfff 0xfff 0xfff 0x19b 0xfff syscon nr __NR_mincore 0x001b 0x00e8 0x200004e 0x004e 0x004e 0x004e 0x04e 0xfff syscon nr __NR_madvise 0x001c 0x00e9 0x200004b 0x004b 0x004b 0x004b 0x04b 0xfff @@ -1792,7 +1795,7 @@ syscon nr __NR_ioprio_get 0x00fc 0x001f 0xfff 0xfff 0xfff 0xfff 0 syscon nr __NR_inotify_init 0x00fd 0x0fff 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff syscon nr __NR_inotify_add_watch 0x00fe 0x0fff 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff syscon nr __NR_inotify_rm_watch 0x00ff 0x0fff 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff -syscon nr __NR_openat 0x0101 0x0038 0x20001cf 0x01cf 0x01f3 0x0141 0x1d4 0xfff +syscon nr __NR_openat 0x0101 0x0038 0x20001d0 0x01cf 0x01f3 0x0141 0x1d4 0xfff # openat_nocancel() on XNU syscon nr __NR_mkdirat 0x0102 0x0022 0x20001db 0x01db 0x01f0 0x013e 0x1cd 0xfff syscon nr __NR_fchownat 0x0104 0x0036 0x20001d4 0x01d4 0x01eb 0x013b 0x1d0 0xfff syscon nr __NR_utime 0x0084 0x0062 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff diff --git a/libc/sysv/consts/ENOEXEC.S b/libc/sysv/consts/ENOEXEC.S index 4b9c0d9d0..0283dc55a 100644 --- a/libc/sysv/consts/ENOEXEC.S +++ b/libc/sysv/consts/ENOEXEC.S @@ -1,2 +1,5 @@ #include "libc/sysv/consts/syscon.internal.h" .syscon errno,ENOEXEC,8,8,8,8,8,8,8,193 +#ifdef __x86_64__ +.yoink kDos2Errno.ENOEXEC +#endif diff --git a/libc/sysv/consts/_MINSIGSTKSZ.S b/libc/sysv/consts/_MINSIGSTKSZ.S new file mode 100644 index 000000000..b55cbcca9 --- /dev/null +++ b/libc/sysv/consts/_MINSIGSTKSZ.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_MINSIGSTKSZ,2048,2048,32768,32768,4096,12288,8192,2048 diff --git a/libc/sysv/consts/_SIGSTKSZ.S b/libc/sysv/consts/_SIGSTKSZ.S new file mode 100644 index 000000000..6347f2877 --- /dev/null +++ b/libc/sysv/consts/_SIGSTKSZ.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_SIGSTKSZ,8192,2048,131072,131072,36864,28672,40960,8192 diff --git a/libc/sysv/consts/__NR_open.S b/libc/sysv/consts/__NR_open.S index e279f4c3f..55a6f7a02 100644 --- a/libc/sysv/consts/__NR_open.S +++ b/libc/sysv/consts/__NR_open.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon nr,__NR_open,0x0002,0x00b4,0x2000005,0x0005,0x0005,0x0005,0x005,0xfff +.syscon nr,__NR_open,0x0002,0x00b4,0x200018e,0x0005,0x0005,0x0005,0x005,0xfff diff --git a/libc/sysv/consts/__NR_openat.S b/libc/sysv/consts/__NR_openat.S index e8a306108..415bf41db 100644 --- a/libc/sysv/consts/__NR_openat.S +++ b/libc/sysv/consts/__NR_openat.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon nr,__NR_openat,0x0101,0x0038,0x20001cf,0x01cf,0x01f3,0x0141,0x1d4,0xfff +.syscon nr,__NR_openat,0x0101,0x0038,0x20001d0,0x01cf,0x01f3,0x0141,0x1d4,0xfff diff --git a/libc/sysv/consts/__NR_sched_yield.S b/libc/sysv/consts/__NR_sched_yield.S index 70381b9b4..bd978ec92 100644 --- a/libc/sysv/consts/__NR_sched_yield.S +++ b/libc/sysv/consts/__NR_sched_yield.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon nr,__NR_sched_yield,0x0018,0x007c,0x200005d,0x005d,0x014b,0x012a,0x15e,0xfff +.syscon nr,__NR_sched_yield,0x0018,0x007c,0x2000197,0x005d,0x014b,0x012a,0x15e,0xfff diff --git a/libc/sysv/consts/__NR_write.S b/libc/sysv/consts/__NR_write.S index 2a8fa8b14..4cc1a5413 100644 --- a/libc/sysv/consts/__NR_write.S +++ b/libc/sysv/consts/__NR_write.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon nr,__NR_write,0x0001,0x0040,0x2000004,0x0004,0x0004,0x0004,0x004,0xfff +.syscon nr,__NR_write,0x0001,0x0040,0x200018d,0x0004,0x0004,0x0004,0x004,0xfff diff --git a/libc/sysv/consts/ss.h b/libc/sysv/consts/ss.h index c0109f677..4cc3d22cf 100644 --- a/libc/sysv/consts/ss.h +++ b/libc/sysv/consts/ss.h @@ -4,6 +4,8 @@ COSMOPOLITAN_C_START_ extern const int SS_DISABLE; +extern const int _SIGSTKSZ; +extern const int _MINSIGSTKSZ; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ @@ -13,5 +15,4 @@ COSMOPOLITAN_C_END_ #define SS_ONSTACK 1 #define SS_DISABLE SS_DISABLE - #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SS_H_ */ diff --git a/libc/sysv/gen.sh b/libc/sysv/gen.sh index 6a637b58d..f1d5f14c4 100644 --- a/libc/sysv/gen.sh +++ b/libc/sysv/gen.sh @@ -33,7 +33,7 @@ scall() { arm_linux=$(($3 + 0)) arm_xnu=$((($amd & 0xfff000) >> 12)) if [ $arm_xnu != 4095 ]; then - arm_xnu=$(($arm_xnu & 0x7ff)) + arm_xnu=$(($arm_xnu & 0xfff)) fi shift 3 set -- "$name" "$amd" "$arm_linux" "$arm_xnu" "$*" diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 69bfa487e..978c448db 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -149,8 +149,8 @@ systemfive_cancellable_end: // i/o calls park here for long time jmp 4f // now we are in fact cancelled systemfive_cancel: // SIGTHR will jump here too pop %rbp -4: jmp _pthread_cancel_sys // tail call - .weak _pthread_cancel_sys // must be linked if we're cancelled +4: jmp _pthread_cancel_ack // tail call + .weak _pthread_cancel_ack // must be linked if we're cancelled #if IsModeDbg() not_a_cancellation_point: // need BEGIN/END_CANCELLATION_POINT nop diff --git a/libc/sysv/sysv.c b/libc/sysv/sysv.c index 4a47693fe..2c68e2f69 100644 --- a/libc/sysv/sysv.c +++ b/libc/sysv/sysv.c @@ -39,7 +39,7 @@ register long cosmo_tls_register asm("x28"); void report_cancellation_point(void); dontinline long systemfive_cancel(void) { - return _pthread_cancel_sys(); + return _weaken(_pthread_cancel_ack)(); } // special region of executable memory where cancellation is safe @@ -49,16 +49,16 @@ dontinline long systemfive_cancellable(void) { // plus (2) cancellations aren't disabled struct PosixThread *pth = 0; struct CosmoTib *tib = __get_tls(); - if (cosmo_tls_register && // - _weaken(_pthread_cancel_sys) && // - !(tib->tib_flags & PT_NOCANCEL) && // + if (cosmo_tls_register && // + _weaken(_pthread_cancel_ack) && // (pth = (struct PosixThread *)tib->tib_pthread)) { // check if cancellation is already pending - if (atomic_load_explicit(&pth->cancelled, memory_order_acquire)) { + if (!(pth->pt_flags & PT_NOCANCEL) && + atomic_load_explicit(&pth->cancelled, memory_order_acquire)) { return systemfive_cancel(); } #if IsModeDbg() - if (!(tib->tib_flags & PT_INCANCEL)) { + if (!(pth->flags & PT_INCANCEL)) { if (_weaken(report_cancellation_point)) { _weaken(report_cancellation_point)(); } @@ -87,7 +87,7 @@ dontinline long systemfive_cancellable(void) { } // check if i/o call was interrupted by sigthr - if (pth && x0 == -EINTR && + if (pth && x0 == -EINTR && !(pth->pt_flags & PT_NOCANCEL) && atomic_load_explicit(&pth->cancelled, memory_order_acquire)) { return systemfive_cancel(); } diff --git a/libc/sysv/sysv.mk b/libc/sysv/sysv.mk index 30f97f15d..00a96f3e8 100644 --- a/libc/sysv/sysv.mk +++ b/libc/sysv/sysv.mk @@ -80,7 +80,9 @@ o/$(MODE)/libc/sysv/sysret.o \ o/$(MODE)/libc/sysv/errfun2.o \ o/$(MODE)/libc/sysv/sysret.o: private \ CFLAGS += \ - $(NO_MAGIC) + -ffreestanding \ + -fno-stack-protector \ + -fno-sanitize=all ifeq ($(ARCH),aarch64) o/$(MODE)/libc/sysv/sysv.o: private \ diff --git a/libc/testlib/aspect.internal.h b/libc/testlib/aspect.internal.h new file mode 100644 index 000000000..a3c26dec9 --- /dev/null +++ b/libc/testlib/aspect.internal.h @@ -0,0 +1,21 @@ +#ifndef COSMOPOLITAN_LIBC_TESTLIB_ASPECT_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_TESTLIB_ASPECT_INTERNAL_H_ +#include "libc/intrin/dll.h" +#include "libc/testlib/testlib.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define TESTASPECT_CONTAINER(e) DLL_CONTAINER(struct TestAspect, elem, e) + +struct TestAspect { + bool once; + void (*setup)(const testfn_t *); + void (*teardown)(const testfn_t *); + struct Dll elem; +}; + +extern struct Dll *testlib_aspects; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_TESTLIB_ASPECT_INTERNAL_H_ */ diff --git a/libc/testlib/benchrunner.c b/libc/testlib/benchrunner.c index 273342092..613264373 100644 --- a/libc/testlib/benchrunner.c +++ b/libc/testlib/benchrunner.c @@ -17,11 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/log/log.h" #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/runtime.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/mlock.h" #include "libc/sysv/consts/o.h" @@ -50,11 +54,15 @@ void testlib_benchwarmup(void) { void EnableCruiseControlForCool(void) { int fd, micros = 10; - if ((fd = open("/dev/cpu_dma_latency", O_WRONLY)) != -1) { - write(fd, µs, sizeof(micros)); - fcntl(fd, F_DUPFD_CLOEXEC, 123); - close(fd); + if (!IsLinux()) return; + BLOCK_CANCELLATIONS; + if ((fd = __sys_openat(AT_FDCWD, "/dev/cpu_dma_latency", O_WRONLY, 0)) != + -1) { + sys_write(fd, µs, sizeof(micros)); + sys_fcntl(fd, F_DUPFD_CLOEXEC, 123, __sys_fcntl); + sys_close(fd); } + ALLOW_CANCELLATIONS; } /** diff --git a/libc/testlib/combo.S b/libc/testlib/combo.S deleted file mode 100644 index 34279d24c..000000000 --- a/libc/testlib/combo.S +++ /dev/null @@ -1,41 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/macros.internal.h" - -// Decentralized section for test combo registration. -// -// @see ape/ape.lds - .section .piro.relo.sort.combo.1,"aw",@progbits - .type __combo_start,@object - .type __combo_end,@object - .globl __combo_start,__combo_end - .hidden __combo_start,__combo_end - .byte 0 - .balign __SIZEOF_POINTER__ - .underrun -__combo_start: - .previous/* - ... - decentralized content - ... - */.section .piro.relo.sort.combo.3,"aw",@progbits -__combo_end: - .quad 0 - .overrun - .previous diff --git a/libc/testlib/comborunner.c b/libc/testlib/comborunner.c deleted file mode 100644 index b0150ba4b..000000000 --- a/libc/testlib/comborunner.c +++ /dev/null @@ -1,99 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/safemacros.internal.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/testlib/testlib.h" - -struct ComboGroup { - unsigned entry; - unsigned i; - unsigned n; -}; - -struct ComboProduct { - unsigned n; - struct ComboGroup groups[]; -}; - -mallocesque struct ComboProduct *testlib_setupcomboproduct( - const struct TestFixture *start, const struct TestFixture *end) { - unsigned i, j, entrycount; - struct ComboProduct *product; - entrycount = testlib_countfixtures(start, end); - product = calloc( - sizeof(struct ComboProduct) + entrycount * sizeof(struct ComboGroup), 1); - for (j = i = 0; i < entrycount; ++i) { - if (j && strcmp(start[product->groups[j - 1].entry].group, - start[i].group) == 0) { - product->groups[j - 1].n++; - } else { - ++j; - product->groups[j - 1].entry = i; - product->groups[j - 1].n = 1; - } - } - product->n = j; - return product; -} - -static void testlib_describecombo(struct ComboProduct *product, - const struct TestFixture *combos) { - char *p = &g_fixturename[0]; - char *pe = p + sizeof(g_fixturename); - for (unsigned i = 0; i < product->n && p < pe; ++i) { - const char *sep = i ? ", " : ""; - const struct TestFixture *e = - &combos[product->groups[i].entry + product->groups[i].i]; - p += max(0, snprintf(p, pe - p, "%s%s=%s", sep, e->group, e->name)); - } -} - -static void testlib_callcombos(struct ComboProduct *product, - const struct TestFixture *combos, - const testfn_t *test_start, - const testfn_t *test_end) { - for (;;) { - testlib_describecombo(product, combos); - for (unsigned i = 0; i < product->n; ++i) { - combos[product->groups[i].entry + product->groups[i].i].fn(); - } - for (unsigned i = product->n;; --i) { - if (!i) return; - if (++product->groups[i - 1].i < product->groups[i - 1].n) break; - product->groups[i - 1].i = 0; - } - testlib_runtestcases(test_start, test_end, NULL); - } -} - -/** - * Runs Cartesian product of COMBO() fixtures registered with linker. - * @see ape/ape.lds - * @see libc/testlib/testlib.h - */ -void testlib_runcombos(const testfn_t *test_start, const testfn_t *test_end, - const struct TestFixture *combo_start, - const struct TestFixture *combo_end) { - struct ComboProduct *product; - product = testlib_setupcomboproduct(combo_start, combo_end); - testlib_callcombos(product, combo_start, test_start, test_end); - free(product); -} diff --git a/libc/testlib/ezbenchcontrol.c b/libc/testlib/ezbenchcontrol.c index 2f1d7da2b..f6526ddd2 100644 --- a/libc/testlib/ezbenchcontrol.c +++ b/libc/testlib/ezbenchcontrol.c @@ -16,8 +16,9 @@ │ 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/intrin/kprintf.h" -#include "libc/stdio/stdio.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -25,8 +26,9 @@ static bool once; static double g_ezbenchcontrol; double __testlib_ezbenchcontrol(void) { + char ibuf[12]; + int Core, Tries, Interrupts; if (!once) { - int Core, Tries, Interrupts; Tries = 0; do { __testlib_yield(); @@ -37,10 +39,11 @@ double __testlib_ezbenchcontrol(void) { } while (++Tries < 10 && (__testlib_getcore() != Core && __testlib_getinterrupts() > Interrupts)); if (Tries == 10) { - fputs("warning: failed to accurately benchmark control\n", stderr); + tinyprint(2, "warning: failed to accurately benchmark control\n"); } - kprintf("will subtract benchmark overhead of %g cycles\n\n", - g_ezbenchcontrol); + FormatInt32(ibuf, g_ezbenchcontrol); + tinyprint(2, "will subtract benchmark overhead of ", ibuf, " cycles\n\n", + NULL); once = true; } return g_ezbenchcontrol; diff --git a/libc/testlib/ezbenchreport.c b/libc/testlib/ezbenchreport.c index bd9fe7ce3..f29631b32 100644 --- a/libc/testlib/ezbenchreport.c +++ b/libc/testlib/ezbenchreport.c @@ -16,74 +16,57 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" #include "libc/math.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/testlib/testlib.h" -#include "libc/time/time.h" -#include "libc/x/x.h" - -__static_yoink("strnwidth"); void __testlib_ezbenchreport(const char *form, double c1, double c2) { - long ns1, ns2; __warn_if_powersave(); - ns1 = lrintl(ConvertTicksToNanos(c1)); - ns2 = lrintl(ConvertTicksToNanos(c2)); - (fprintf)(stderr, - __veil("r", " * %-19s l: %,9luc %,9luns m: %,9luc %,9luns\n"), - form, lrint(c1), ns1, lrint(c2), ns2); + kprintf(" * %-19s l: %,9luc %,9luns m: %,9luc %,9luns\n", form, + lrint(c1), lrint(c1 / 3), lrint(c2), lrint(c2 / 3)); } void __testlib_ezbenchreport_n(const char *form, char z, size_t n, double c) { - char msg[128]; + long cn, lat; uint64_t bps; - long double cn, lat; + char msg[128]; __warn_if_powersave(); - (snprintf)(msg, sizeof(msg), "%s %c=%d", form, z, n); - cn = ConvertTicksToNanos(c); + ksnprintf(msg, sizeof(msg), "%s %c=%d", form, z, n); + cn = lrint(c / 3); if (!n) { - (fprintf)(stderr, "\n"); - (fprintf)(stderr, " * %-28s", msg); + kprintf("\n"); + kprintf(" * %-28s", msg); if (cn < 1) { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)(cn * 1024), - "picoseconds"); + kprintf(" %'9lu %-12s", (int64_t)(cn * 1024), "picoseconds"); } else if (cn > 1024) { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)(cn / 1024), - "microseconds"); + kprintf(" %'9lu %-12s", (int64_t)(cn / 1024), "microseconds"); } else { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)cn, - "nanoseconds"); + kprintf(" %'9lu %-12s", (int64_t)cn, "nanoseconds"); } } else { - (fprintf)(stderr, " * %-28s", msg); + kprintf(" * %-28s", msg); bps = n / cn * 1e9; lat = cn / n; if (lat < 1e-3) { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), - (int64_t)(lat * 1024 * 1024), "fs/byte"); + kprintf(" %'9lu %-12s", (int64_t)(lat * 1024 * 1024), "fs/byte"); } else if (lat < 1) { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)(lat * 1024), - "ps/byte"); + kprintf(" %'9lu %-12s", (int64_t)(lat * 1024), "ps/byte"); } else if (lat > 1024) { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)(lat / 1024), - "µs/byte"); + kprintf(" %'9lu %-12s", (int64_t)(lat / 1024), "µs/byte"); } else { - (fprintf)(stderr, __veil("r", " %,9lu %-12s"), (int64_t)lat, "ns/byte"); + kprintf(" %'9lu %-12s", (int64_t)lat, "ns/byte"); } if (bps < 10 * 1000) { - (fprintf)(stderr, __veil("r", " %,9lu b/s"), bps); + kprintf(" %'9lu b/s", bps); } else if (bps < 10 * 1000 * 1024) { - (fprintf)(stderr, __veil("r", " %,9lu kb/s"), bps / 1024); + kprintf(" %'9lu kb/s", bps / 1024); } else if (bps < 10ul * 1000 * 1024 * 1024) { - (fprintf)(stderr, __veil("r", " %,9lu mb/s"), bps / (1024 * 1024)); + kprintf(" %'9lu mb/s", bps / (1024 * 1024)); } else if (bps < 10ul * 1000 * 1024 * 1024 * 1024) { - (fprintf)(stderr, __veil("r", " %,9lu GB/s"), bps / (1024 * 1024 * 1024)); + kprintf(" %'9lu GB/s", bps / (1024 * 1024 * 1024)); } else { - (fprintf)(stderr, __veil("r", " %,9lu TB/s"), - bps / (1024ul * 1024 * 1024 * 1024)); + kprintf(" %'9lu TB/s", bps / (1024ul * 1024 * 1024 * 1024)); } } - (fprintf)(stderr, "\n", form); + kprintf("\n", form); } diff --git a/libc/testlib/ezbenchwarn.c b/libc/testlib/ezbenchwarn.c index 768b1d4c6..6064ee3a0 100644 --- a/libc/testlib/ezbenchwarn.c +++ b/libc/testlib/ezbenchwarn.c @@ -16,11 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" +#include "libc/calls/calls.h" #include "libc/testlib/testlib.h" void __testlib_ezbenchwarn(const char *msg) { - fputs("warning: failed to accurately benchmark", stderr); - fputs(msg, stderr); - fputc('\n', stderr); + tinyprint(2, "warning: failed to accurately benchmark", msg, "\n", NULL); } diff --git a/libc/testlib/fixturerunner.c b/libc/testlib/fixturerunner.c index 6d9205b33..f1209ef93 100644 --- a/libc/testlib/fixturerunner.c +++ b/libc/testlib/fixturerunner.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/internal.h" -#include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" @@ -37,8 +37,9 @@ void testlib_runfixtures(const testfn_t *test_start, const testfn_t *test_end, unsigned i, count; count = testlib_countfixtures(fixture_start, fixture_end); for (i = 0; i < count && !g_testlib_failed; ++i) { - (snprintf)(g_fixturename, sizeof(g_fixturename), "%s_%s", - fixture_start[i].group, fixture_start[i].name); + strlcpy(g_fixturename, fixture_start[i].group, sizeof(g_fixturename)); + strlcat(g_fixturename, "_", sizeof(g_fixturename)); + strlcat(g_fixturename, fixture_start[i].name, sizeof(g_fixturename)); fixture_start[i].fn(); testlib_runtestcases(test_start, test_end, NULL); } diff --git a/libc/testlib/formatint.c b/libc/testlib/formatint.c index fdfc8b345..1b49535fc 100644 --- a/libc/testlib/formatint.c +++ b/libc/testlib/formatint.c @@ -16,23 +16,48 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/bits.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" -#include "libc/testlib/testlib.h" +#include "libc/atomic.h" +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/atomic.h" +#include "libc/str/str.h" -static size_t sbufi_; -static char sbufs_[2][256]; +#define STRS 4 +#define BYTES 128 + +static atomic_uint bufi; +static char bufs[STRS][BYTES]; char *testlib_formatint(intptr_t x) { - char *str = sbufi_ < ARRAYLEN(sbufs_) ? sbufs_[sbufi_++] : malloc(256); - char *p = str; - p += sprintf(p, "%ld\t(or %#lx", x, x); - if (0 <= x && x < 256) { - p += sprintf(p, " or %#`c", (unsigned char)x); + int i = atomic_fetch_add(&bufi, 1) % STRS; + char *p = bufs[i]; + p = FormatInt64(p, x); + p = stpcpy(p, " (or "); + p = FormatHex64(p, x, 1); + if (0 <= x && x <= 255) { + p = stpcpy(p, " or '"); + if (!isascii(x) || iscntrl(x)) { + *p++ = '\\'; + if (x > 7) { + if (x > 070) { + *p++ = '0' + ((x & 0700) >> 6); + } + *p++ = '0' + ((x & 070) >> 3); + } + *p++ = '0' + (x & 7); + } else if (x == '\\' || x == '\'') { + *p++ = '\\'; + *p++ = x; + } else { + *p++ = x; + } + *p++ = '\''; + } + if (_strerrno(x)) { + p = stpcpy(p, " or "); + p = stpcpy(p, _strerrno(x)); } *p++ = ')'; *p++ = '\0'; - return strdup(str); + return bufs[i]; } diff --git a/libc/testlib/formatstr.c b/libc/testlib/formatstr.c index f694ae5b9..8b1843288 100644 --- a/libc/testlib/formatstr.c +++ b/libc/testlib/formatstr.c @@ -16,31 +16,98 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/bits.h" #include "libc/str/str.h" +#include "libc/str/utf16.h" #include "libc/testlib/testlib.h" -#include "libc/x/xasprintf.h" + +#define STRS 4 +#define BYTES 64 + +#define APPEND(c) \ + do { \ + if (j + 1 < BYTES) { \ + bufs[i][j++] = c; \ + } \ + } while (0) + +static atomic_uint bufi; +static char bufs[STRS][BYTES]; + +static int AppendWide(wint_t x, int i, int j) { + uint64_t w; + if (isascii(x) && iscntrl(x)) { + APPEND('\\'); + APPEND('0' + ((x & 0700) >> 6)); + APPEND('0' + ((x & 070) >> 3)); + APPEND('0' + (x & 7)); + } else if (x == '\\' || x == '\'') { + APPEND('\\'); + APPEND(x); + } else { + w = tpenc(x); + while (w) { + APPEND(w); + w >>= 8; + } + } + return j; +} /** * Turns string into code. */ -char *testlib_formatstr(size_t cw, const void *s, int n) { - if (s) { - switch (cw) { - case 1: - if (n == -1) n = s ? strlen(s) : 0; - return xasprintf("%`'.*s", n, s); - case 2: - if (n == -1) n = s ? strlen16(s) : 0; - return xasprintf("%`'.*hs", n, s); - case 4: - if (n == -1) n = s ? wcslen(s) : 0; - return xasprintf("%`'.*ls", n, s); - default: - abort(); +char *testlib_formatstr(size_t cw, const void *p, int n) { + int i, j = 0; + if (!p) return "NULL"; + i = atomic_fetch_add(&bufi, 1) % STRS; + switch (cw) { + case 1: { + const char *s = p; + if (n < 0) n = s ? strlen(s) : 0; + const char *se = s + n; + APPEND('"'); + while (s < se) { + j = AppendWide(*s++ & 255, i, j); + } + break; } - } else { - return strdup("NULL"); + case 2: { + const char16_t *s = p; + if (n < 0) n = s ? strlen16(s) : 0; + const char16_t *se = s + n; + APPEND('u'); + APPEND('"'); + while (s < se) { + wint_t x = *s++ & 0xffff; + if (IsUtf16Cont(x)) continue; + if (!IsUcs2(x) && s < se) { + wint_t y = *s++ & 0xffff; + x = MergeUtf16(x, y); + } + j = AppendWide(x, i, j); + } + break; + } + case 4: { + const wchar_t *s = p; + if (n < 0) n = s ? wcslen(s) : 0; + const wchar_t *se = s + n; + APPEND('L'); + APPEND('"'); + while (s < se) { + j = AppendWide(*s++, i, j); + } + break; + } + default: + notpossible; } + APPEND('"'); + bufs[i][j] = 0; + if (j == BYTES - 1) { + WRITE32LE(bufs[i] + j - 4, READ32LE("...\"")); + } + return bufs[i]; } diff --git a/libc/testlib/quota.c b/libc/testlib/quota.c deleted file mode 100644 index 1fcd21318..000000000 --- a/libc/testlib/quota.c +++ /dev/null @@ -1,109 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/struct/sigaction.h" -#include "libc/errno.h" -#include "libc/intrin/bits.h" -#include "libc/intrin/bsr.h" -#include "libc/intrin/kprintf.h" -#include "libc/log/backtrace.internal.h" -#include "libc/log/internal.h" -#include "libc/log/libfatal.internal.h" -#include "libc/log/log.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/sig.h" -#include "libc/testlib/testlib.h" -#include "third_party/dlmalloc/dlmalloc.h" - -static dontasan dontubsan relegated uint64_t CountMappedBytes(void) { - size_t i; - uint64_t x, y; - for (x = i = 0; i < _mmi.i; ++i) { - y = _mmi.p[i].y - _mmi.p[i].x; - x += (y + 1) << 16; - } - return x; -} - -static relegated void DieBecauseOfQuota(int rc, const char *message) { - char hostname[32]; - stpcpy(hostname, "unknown"); - gethostname(hostname, sizeof(hostname)); - kprintf("%s on %s pid %d\n", message, hostname, (long)getpid()); - PrintBacktraceUsingSymbols(2, 0, GetSymbolTable()); - _Exit(rc); -} - -static relegated void OnXcpu(int sig) { - __restore_tty(); - DieBecauseOfQuota(23, "\n\nSIGXCPU: ran out of cpu"); -} - -static relegated void OnXfsz(int sig) { - __restore_tty(); - DieBecauseOfQuota(25, "\n\nSIGXFSZ: exceeded maximum file size"); -} - -static unsigned long roundup2pow(unsigned long x) { - return x > 1 ? 2ul << _bsrl(x - 1) : x ? 1 : 0; -} - -relegated void __oom_hook(size_t request) { - int e; - uint64_t toto, newlim; - __restore_tty(); - e = errno; - toto = CountMappedBytes(); - kprintf("\n\nWE REQUIRE MORE VESPENE GAS"); - if (e != ENOMEM) kprintf(" (%s)", strerror(e)); - if (IsRunningUnderMake()) { - newlim = toto + request; - newlim += newlim >> 1; - newlim = roundup2pow(newlim); - kprintf("FIX CODE OR TUNE QUOTA += -M%dm\n", newlim / (1024 * 1024)); - } - kprintf("\n"); - __print_maps(); - kprintf("\nTHE STRAW THAT BROKE THE CAMEL'S BACK\n"); - PrintBacktraceUsingSymbols(2, 0, GetSymbolTable()); - PrintSystemMappings(2); - _Exit(42); -} - -static textstartup void InstallQuotaHandlers(void) { - int e; - struct sigaction sa; - e = errno; - sa.sa_flags = 0; - sa.sa_handler = OnXcpu; - sigemptyset(&sa.sa_mask); - sigaction(SIGXCPU, &sa, 0); - sa.sa_handler = OnXfsz; - sigaction(SIGXFSZ, &sa, 0); - GetSymbolTable(); /* for effect in case we oom */ - errno = e; -} - -const void *const testlib_quota_handlers[] initarray = { - InstallQuotaHandlers, -}; diff --git a/libc/testlib/runner.c b/libc/testlib/runner.c index a9091c9dd..9da293237 100644 --- a/libc/testlib/runner.c +++ b/libc/testlib/runner.c @@ -30,14 +30,9 @@ */ void testlib_runalltests(void) { if ((intptr_t)__testcase_end > (intptr_t)__testcase_start) { - if (testlib_countfixtures(__combo_start, __combo_end)) { - testlib_runcombos(__testcase_start, __testcase_end, __combo_start, - __combo_end); - } else { - testlib_runtestcases(__testcase_start, __testcase_end, NULL); - testlib_runfixtures(__testcase_start, __testcase_end, __fixture_start, - __fixture_end); - testlib_finish(); - } + testlib_runtestcases(__testcase_start, __testcase_end, NULL); + testlib_runfixtures(__testcase_start, __testcase_end, __fixture_start, + __fixture_end); + testlib_finish(); } } diff --git a/libc/testlib/showerror.c b/libc/testlib/showerror.c index 774272d4b..b4ef59160 100644 --- a/libc/testlib/showerror.c +++ b/libc/testlib/showerror.c @@ -16,17 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "ape/sections.internal.h" #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/safemacros.internal.h" +#include "libc/intrin/weaken.h" #include "libc/log/color.internal.h" #include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" @@ -36,24 +40,33 @@ const char *testlib_showerror_func; const char *testlib_showerror_macro; const char *testlib_showerror_symbol; -// TODO(jart): Pay off tech debt re duplication +static void Free(void *p) { + if (_weaken(free) && (long)p >= (long)_end) { + _weaken(free)(p); + } +} + void testlib_showerror(const char *file, int line, const char *func, const char *method, const char *symbol, const char *code, char *v1, char *v2) { - char hostname[128]; - __stpcpy(hostname, "unknown"); + char hostname[128], linestr[12], pidstr[12], tidstr[12]; + stpcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - kprintf("%serror%s%s:%s:%d%s: %s() in %s(%s) on %s pid %d tid %d\n" - "\t%s\n" - "\t\tneed %s %s\n" - "\t\t got %s\n" - "\t%s%s\n" - "\t%s%s\n", - RED2, UNBOLD, BLUE1, file, line, RESET, method, func, g_fixturename, - hostname, getpid(), gettid(), code, v1, symbol, v2, SUBTLE, - strerror(errno), GetProgramExecutableName(), RESET); - free(v1); - free(v2); + FormatInt32(linestr, line); + FormatInt32(pidstr, getpid()); + FormatInt32(tidstr, gettid()); + tinyprint(2, RED2, "error", UNBOLD, ":", BLUE1, // + file, ":", linestr, RESET, ": ", // + method, "() in ", func, "(", g_fixturename, ") ", // + "on ", hostname, " pid ", pidstr, " tid ", tidstr, "\n", // + "\t", code, "\n", // + "\t\tneed ", v1, " ", symbol, "\n", // + "\t\t got ", v2, "\n", // + "\t", SUBTLE, strerror(errno), "\n", // + "\t", __argv[0], RESET, "\n", // + NULL); + Free(v1); + Free(v2); } static void testlib_showerror_(int line, // @@ -63,36 +76,41 @@ static void testlib_showerror_(int line, // char *FREED_got, // const char *fmt, // va_list va) { - int e; - char hostname[128]; - e = errno; - if (gethostname(hostname, sizeof(hostname))) { - __stpcpy(hostname, "unknown"); - } - kprintf("%serror%s:%s%s:%d%s: %s(%s) on %s pid %d tid %d\n" - "\t%s(%s, %s)\n", - RED2, UNBOLD, BLUE1, testlib_showerror_file, line, RESET, - testlib_showerror_func, g_fixturename, hostname, getpid(), gettid(), - testlib_showerror_macro, wantcode, gotcode); + int e = errno; + char hostname[128], linestr[12], pidstr[12], tidstr[12]; + stpcpy(hostname, "unknown"); + gethostname(hostname, sizeof(hostname)); + FormatInt32(linestr, line); + FormatInt32(pidstr, getpid()); + FormatInt32(tidstr, gettid()); + tinyprint( // + 2, RED2, "error", UNBOLD, BLUE1, ":", // + testlib_showerror_file, ":", linestr, RESET, ": ", // + testlib_showerror_func, "(", g_fixturename, ") ", // + "on ", hostname, " pid ", pidstr, " tid ", tidstr, "\n", // + "\t", testlib_showerror_macro, "(", wantcode, ", ", gotcode, ")\n", // + NULL); if (wantcode) { - kprintf("\t\tneed %s %s\n" - "\t\t got %s\n", - FREED_want, testlib_showerror_symbol, FREED_got); + tinyprint(2, "\t\tneed ", FREED_want, " ", testlib_showerror_symbol, "\n", + "\t\t got ", FREED_got, "\n", NULL); } else { - kprintf("\t\t→ %s%s\n", testlib_showerror_symbol, FREED_want); + tinyprint(2, "\t\t→ ", testlib_showerror_symbol, FREED_want, "\n", NULL); } if (!isempty(fmt)) { - kprintf("\t"); - kvprintf(fmt, va); - kprintf("\n"); + if (_weaken(kvprintf)) { + tinyprint(2, "\t"); + _weaken(kvprintf)(fmt, va); + tinyprint(2, "\n"); + } else { + tinyprint(2, "\t[missing kvprintf]\n"); + } } - kprintf("\t%s%s%s\n" - "\t%s%s @ %s%s\n", - SUBTLE, strerror(e), RESET, SUBTLE, - firstnonnull(program_invocation_name, "unknown"), hostname, RESET); - /* free(FREED_want); */ - /* free(FREED_got); */ + tinyprint(2, "\t", SUBTLE, strerror(e), RESET, "\n\t", SUBTLE, + firstnonnull(program_invocation_name, "unknown"), " @ ", hostname, + RESET, "\n", NULL); ++g_testlib_failed; + Free(FREED_want); + Free(FREED_got); } void testlib_showerror_assert_eq(int line, // diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index 432ce727a..397a0dade 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -29,17 +29,6 @@ COSMOPOLITAN_C_START_ __static_yoink("__fixture_start"); \ __FIXTURE("fixture", SUITE, NAME) -/** - * Registers explosive fixture with linker. - * - * All tests will run an additional time for each set of entries in the - * Cartesian product of groups. That makes this similar to fixture, but - * more appropriate for testing pure code (i.e. no syscalls) like math. - */ -#define COMBO(GROUP, ENTRY) \ - __static_yoink("__combo_start"); \ - __FIXTURE("combo", GROUP, ENTRY) - /** * Declares benchmark function. * @@ -104,13 +93,10 @@ COSMOPOLITAN_C_START_ /** * Enables setup and teardown of test directories. * - * If the linker says this symbol exists then, regardless of its value, - * a unique test directory will be created at the start of each test, - * the test will be run with that directory as its working directory, - * and if the test succeeds it'll be removed along with any contents. + * These should be called from SetUpOnce(). */ -extern char testlib_enable_tmp_setup_teardown; -extern char testlib_enable_tmp_setup_teardown_once; +void testlib_enable_tmp_setup_teardown(void); +void testlib_enable_tmp_setup_teardown_once(void); /** * User-defined test setup function. @@ -357,7 +343,6 @@ extern const char *testlib_showerror_func; /* set by macros */ extern const testfn_t __bench_start[], __bench_end[]; extern const testfn_t __testcase_start[], __testcase_end[]; extern const struct TestFixture __fixture_start[], __fixture_end[]; -extern const struct TestFixture __combo_start[], __combo_end[]; void testlib_showerror_assert_eq(int, const char *, const char *, char *, char *, const char *, ...) wontreturn; @@ -389,8 +374,6 @@ const char *testlib_strerror(void); void testlib_runallbenchmarks(void); bool testlib_memoryexists(const void *); void testlib_runtestcases(const testfn_t *, const testfn_t *, testfn_t); -void testlib_runcombos(const testfn_t *, const testfn_t *, - const struct TestFixture *, const struct TestFixture *); void testlib_runfixtures(const testfn_t *, const testfn_t *, const struct TestFixture *, const struct TestFixture *); diff --git a/libc/testlib/testlib.mk b/libc/testlib/testlib.mk index 6e2b2d8f3..ac55a121b 100644 --- a/libc/testlib/testlib.mk +++ b/libc/testlib/testlib.mk @@ -19,6 +19,7 @@ LIBC_TESTLIB_A_ASSETS = \ libc/testlib/moby.txt LIBC_TESTLIB_A_HDRS = \ + libc/testlib/aspect.internal.h \ libc/testlib/bench.h \ libc/testlib/blocktronics.h \ libc/testlib/ezbench.h \ @@ -32,7 +33,6 @@ LIBC_TESTLIB_A_HDRS = \ LIBC_TESTLIB_A_SRCS_S = \ libc/testlib/bench.S \ libc/testlib/blocktronics.S \ - libc/testlib/combo.S \ libc/testlib/fixture.S \ libc/testlib/hyperion.S \ libc/testlib/moby.S \ @@ -45,7 +45,6 @@ LIBC_TESTLIB_A_SRCS_C = \ libc/testlib/benchrunner.c \ libc/testlib/binequals.c \ libc/testlib/clearxmmregisters.c \ - libc/testlib/comborunner.c \ libc/testlib/contains.c \ libc/testlib/endswith.c \ libc/testlib/extract.c \ @@ -67,7 +66,6 @@ LIBC_TESTLIB_A_SRCS_C = \ libc/testlib/hexequals.c \ libc/testlib/incrementfailed.c \ libc/testlib/memoryexists.c \ - libc/testlib/quota.c \ libc/testlib/seterrno.c \ libc/testlib/shoulddebugbreak.c \ libc/testlib/showerror.c \ @@ -77,6 +75,7 @@ LIBC_TESTLIB_A_SRCS_C = \ libc/testlib/strerror.c \ libc/testlib/testrunner.c \ libc/testlib/thunks.c \ + libc/testlib/tmptest.c \ libc/testlib/waitforexit.c \ libc/testlib/waitforterm.c \ libc/testlib/yield.c @@ -98,6 +97,7 @@ LIBC_TESTLIB_A_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ @@ -133,8 +133,6 @@ o/$(MODE)/libc/testlib/bench.o: libc/testlib/bench.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/testlib/blocktronics.o: libc/testlib/blocktronics.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/testlib/combo.o: libc/testlib/combo.S - @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/testlib/fixture.o: libc/testlib/fixture.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/testlib/hyperion.o: libc/testlib/hyperion.S diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 9618fed69..ccec6f593 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -16,47 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "ape/sections.internal.h" #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.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/bits.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/dll.h" +#include "libc/intrin/getenv.internal.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" -#include "libc/log/check.h" -#include "libc/log/color.internal.h" -#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/vendor.internal.h" -#include "libc/nexgen32e/x86feature.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" -#include "libc/runtime/sysconf.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/pollfd.h" -#include "libc/stdio/stdio.h" -#include "libc/sysv/consts/ex.h" -#include "libc/sysv/consts/exit.h" +#include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/poll.h" -#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sig.h" +#include "libc/testlib/aspect.internal.h" #include "libc/testlib/testlib.h" -#include "third_party/dlmalloc/dlmalloc.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" #include "third_party/getopt/getopt.internal.h" #define USAGE \ @@ -68,20 +55,15 @@ Flags:\n\ -h show this information\n\ \n" -__static_yoink("__die"); -__static_yoink("GetSymbolByAddr"); -__static_yoink("testlib_quota_handlers"); - static bool runbenchmarks_; -void PrintUsage(int rc, FILE *f) { - fputs("Usage: ", f); - fputs(firstnonnull(program_invocation_name, "unknown"), f); - fputs(USAGE, f); +static void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", firstnonnull(program_invocation_name, "unknown"), + USAGE, NULL); exit(rc); } -void GetOpts(int argc, char *argv[]) { +static void GetOpts(int argc, char *argv[]) { int opt; while ((opt = getopt(argc, argv, "?hbv")) != -1) { switch (opt) { @@ -93,69 +75,9 @@ void GetOpts(int argc, char *argv[]) { break; case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(0, 1); default: - PrintUsage(EX_USAGE, stderr); - } - } -} - -static void EmptySignalMask(void) { - sigset_t ss; - sigemptyset(&ss); - sigprocmask(SIG_SETMASK, &ss, 0); -} - -static void FixIrregularFds(void) { - int e, i, fd, maxfds; - struct rlimit rlim; - struct pollfd *pfds; - for (i = 0; i < 3; ++i) { - if (fcntl(i, F_GETFL) == -1) { - errno = 0; - fd = open("/dev/null", O_RDWR); - CHECK_NE(-1, fd); - if (fd != i) { - close(fd); - } - } - } - // TODO(jart): delete this stuff - if (1) return; - e = errno; - if (!closefrom(3)) return; - errno = e; - if (IsWindows()) { - maxfds = 64; - } else { - maxfds = 256; - if (!getrlimit(RLIMIT_NOFILE, &rlim)) { - maxfds = MIN(maxfds, (uint64_t)rlim.rlim_cur); - } - } - pfds = malloc(maxfds * sizeof(struct pollfd)); - for (i = 0; i < maxfds; ++i) { - pfds[i].fd = i + 3; - pfds[i].events = POLLIN; - } - if (poll(pfds, maxfds, 0) != -1) { - for (i = 0; i < maxfds; ++i) { - if (pfds[i].revents & POLLNVAL) continue; - CHECK_EQ(0, close(pfds[i].fd)); - } - } - free(pfds); -} - -static void SetLimit(int resource, uint64_t soft, uint64_t hard) { - struct rlimit old; - struct rlimit lim = {soft, hard}; - if (resource == 127) return; - if (setrlimit(resource, &lim) == -1) { - if (!getrlimit(resource, &old)) { - lim.rlim_max = MIN(hard, old.rlim_max); - lim.rlim_cur = MIN(soft, lim.rlim_max); - setrlimit(resource, &lim); + PrintUsage(1, 2); } } } @@ -166,34 +88,82 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) { * Generic test program main function. */ dontasan int main(int argc, char *argv[]) { + int fd; + struct Dll *e; + struct TestAspect *a; + __log_level = kLogInfo; GetOpts(argc, argv); + + for (fd = 3; fd < 10; ++fd) { + close(fd); + } + +#ifndef TINY setenv("GDB", "", true); GetSymbolTable(); - - // normalize this process - FixIrregularFds(); - EmptySignalMask(); +#endif ShowCrashReports(); - // now get down to business - g_testlib_shoulddebugbreak = IsDebuggerPresent(false); - if (!IsWindows()) sys_getpid(); // make strace easier to read + // global setup + errno = 0; + STRACE(""); + STRACE("# setting up once"); + if (!IsWindows()) sys_getpid(); testlib_clearxmmregisters(); + if (_weaken(SetUpOnce)) { + _weaken(SetUpOnce)(); + } + for (e = dll_first(testlib_aspects); e; e = dll_next(testlib_aspects, e)) { + a = TESTASPECT_CONTAINER(e); + if (a->once && a->setup) { + a->setup(0); + } + } + + // run tests testlib_runalltests(); + + // run benchmarks if (!g_testlib_failed && runbenchmarks_ && _weaken(testlib_runallbenchmarks)) { _weaken(testlib_runallbenchmarks)(); - if (IsAsan() && !g_testlib_failed) { - CheckForMemoryLeaks(); + } + + // global teardown + STRACE(""); + STRACE("# tearing down once"); + for (e = dll_last(testlib_aspects); e; e = dll_prev(testlib_aspects, e)) { + a = TESTASPECT_CONTAINER(e); + if (a->once && a->teardown) { + a->teardown(0); } - if (!g_testlib_failed && IsRunningUnderMake()) { - return 254; // compile.com considers this 0 and propagates output - } - } else if (IsAsan() && !g_testlib_failed) { + } + if (_weaken(TearDownOnce)) { + _weaken(TearDownOnce)(); + } + + // make sure threads are in a good state + if (_weaken(_pthread_decimate)) { + _weaken(_pthread_decimate)(); + } + if (_weaken(pthread_orphan_np) && !_weaken(pthread_orphan_np)()) { + tinyprint(2, "error: tests ended with threads still active\n", NULL); + _Exit(1); + } + + // check for memory leaks + if (IsAsan() && !g_testlib_failed) { CheckForMemoryLeaks(); } // we're done! - exit(min(255, g_testlib_failed)); + int status = MIN(255, g_testlib_failed); + if (!status && IsRunningUnderMake()) { + return 254; // compile.com considers this 0 and propagates output + } else if (!status && _weaken(pthread_exit)) { + _weaken(pthread_exit)(0); + } else { + return status; + } } diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 6887b2540..f30eaafe8 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -21,7 +21,8 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/dll.h" +#include "libc/intrin/getenv.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/limits.h" @@ -31,17 +32,20 @@ #include "libc/stdio/rand.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/testlib/aspect.internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" #include "libc/x/x.h" -static char g_olddir[PATH_MAX]; -static char g_tmpdir[PATH_MAX]; +struct Dll *testlib_aspects; static pthread_mutex_t testlib_error_lock; void testlib_finish(void) { + char b1[12], b2[12]; if (g_testlib_failed) { - kprintf("%u / %u %s\n", g_testlib_failed, g_testlib_ran, "tests failed"); + FormatInt32(b1, g_testlib_failed); + FormatInt32(b2, g_testlib_ran); + tinyprint(2, b1, " / ", b2, " tests failed\n", NULL); } } @@ -69,37 +73,6 @@ wontreturn void testlib_abort(void) { _Exit(MAX(1, MIN(255, g_testlib_failed))); } -static void SetupTmpDir(void) { - char number[21]; - FormatInt64(number, _rand64() & INT64_MAX); - g_tmpdir[0] = 0; - if (*kTmpPath != '/') { - strlcat(g_tmpdir, g_olddir, sizeof(g_tmpdir)); - strlcat(g_tmpdir, "/", sizeof(g_tmpdir)); - } - strlcat(g_tmpdir, kTmpPath, sizeof(g_tmpdir)); - strlcat(g_tmpdir, program_invocation_short_name, sizeof(g_tmpdir)); - strlcat(g_tmpdir, ".", sizeof(g_tmpdir)); - strlcat(g_tmpdir, number, sizeof(g_tmpdir)); - if (makedirs(g_tmpdir, 0755) || chdir(g_tmpdir)) { - perror(g_tmpdir); - tinyprint(2, "testlib failed to setup tmpdir\n", NULL); - exit(1); - } -} - -static void TearDownTmpDir(void) { - if (chdir(g_olddir)) { - perror(g_olddir); - exit(1); - } - if (rmrf(g_tmpdir)) { - perror(g_tmpdir); - tinyprint(2, "testlib failed to tear down tmpdir\n", NULL); - exit(1); - } -} - /** * Runs all test case functions in sorted order. */ @@ -116,45 +89,41 @@ void testlib_runtestcases(const testfn_t *start, const testfn_t *end, // Test cases are iterable via a decentralized section. Your TEST() // macro inserts .testcase.SUITENAME sections into the binary which // the linker sorts into an array. - // - // @see ape/ape.lds + char host[64]; + struct Dll *e; const testfn_t *fn; - if (_weaken(testlib_enable_tmp_setup_teardown) || - _weaken(testlib_enable_tmp_setup_teardown_once)) { - if (!getcwd(g_olddir, sizeof(g_olddir))) { - perror("getcwd"); - exit(1); - } - } - if (_weaken(testlib_enable_tmp_setup_teardown_once)) { - SetupTmpDir(); - } - if (_weaken(SetUpOnce)) _weaken(SetUpOnce)(); + struct TestAspect *a; + strcpy(host, "unknown"); + gethostname(host, sizeof(host)), errno = 0; for (fn = start; fn != end; ++fn) { STRACE(""); STRACE("# setting up %t", fn); - if (_weaken(testlib_enable_tmp_setup_teardown)) SetupTmpDir(); + for (e = dll_first(testlib_aspects); e; e = dll_next(testlib_aspects, e)) { + a = TESTASPECT_CONTAINER(e); + if (!a->once && a->setup) { + a->setup(fn); + } + } if (_weaken(SetUp)) _weaken(SetUp)(); errno = 0; - if (IsWindows()) { - SetLastError(0); - } + if (IsWindows()) SetLastError(0); if (!IsWindows()) sys_getpid(); if (warmup) warmup(); testlib_clearxmmregisters(); STRACE(""); - STRACE("# running test %t", fn); + STRACE("# running test %t on %s@%s", fn, __getenv(environ, "USER").s, host); (*fn)(); STRACE(""); STRACE("# tearing down %t", fn); if (!IsWindows()) sys_getpid(); - if (_weaken(TearDown)) _weaken(TearDown)(); - if (_weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir(); - } - if (_weaken(TearDownOnce)) { - _weaken(TearDownOnce)(); - } - if (_weaken(testlib_enable_tmp_setup_teardown_once)) { - TearDownTmpDir(); + if (_weaken(TearDown)) { + _weaken(TearDown)(); + } + for (e = dll_last(testlib_aspects); e; e = dll_prev(testlib_aspects, e)) { + a = TESTASPECT_CONTAINER(e); + if (!a->once && a->teardown) { + a->teardown(fn); + } + } } } diff --git a/libc/testlib/tmptest.c b/libc/testlib/tmptest.c new file mode 100644 index 000000000..28859b51b --- /dev/null +++ b/libc/testlib/tmptest.c @@ -0,0 +1,93 @@ +/*-*- 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/assert.h" +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/dll.h" +#include "libc/limits.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/testlib/aspect.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +static struct { + bool is_aspected; + struct TestAspect aspect; + char olddir[PATH_MAX]; + char tmpdir[PATH_MAX]; +} g_tmptest; + +static void SetupTmpDir(const testfn_t *fn) { + char number[21]; + FormatInt64(number, _rand64() & INT64_MAX); + g_tmptest.tmpdir[0] = 0; + if (*__get_tmpdir() != '/') { + strlcat(g_tmptest.tmpdir, g_tmptest.olddir, sizeof(g_tmptest.tmpdir)); + strlcat(g_tmptest.tmpdir, "/", sizeof(g_tmptest.tmpdir)); + } + strlcat(g_tmptest.tmpdir, __get_tmpdir(), sizeof(g_tmptest.tmpdir)); + strlcat(g_tmptest.tmpdir, program_invocation_short_name, + sizeof(g_tmptest.tmpdir)); + strlcat(g_tmptest.tmpdir, ".", sizeof(g_tmptest.tmpdir)); + strlcat(g_tmptest.tmpdir, number, sizeof(g_tmptest.tmpdir)); + if (makedirs(g_tmptest.tmpdir, 0755) || chdir(g_tmptest.tmpdir)) { + perror(g_tmptest.tmpdir); + tinyprint(2, "testlib failed to setup tmpdir\n", NULL); + exit(1); + } +} + +static void TearDownTmpDir(const testfn_t *fn) { + if (chdir(g_tmptest.olddir)) { + perror(g_tmptest.olddir); + exit(1); + } + if (rmrf(g_tmptest.tmpdir)) { + perror(g_tmptest.tmpdir); + tinyprint(2, "testlib failed to tear down tmpdir\n", NULL); + exit(1); + } +} + +static void InstallAspect(void) { + unassert(!g_tmptest.is_aspected); + g_tmptest.is_aspected = true; + g_tmptest.aspect.teardown = TearDownTmpDir; + dll_init(&g_tmptest.aspect.elem); + dll_make_last(&testlib_aspects, &g_tmptest.aspect.elem); + if (!getcwd(g_tmptest.olddir, sizeof(g_tmptest.olddir))) { + perror("getcwd"); + exit(1); + } +} + +void testlib_enable_tmp_setup_teardown(void) { + g_tmptest.aspect.setup = SetupTmpDir; + InstallAspect(); +} + +void testlib_enable_tmp_setup_teardown_once(void) { + g_tmptest.aspect.once = true; + InstallAspect(); + SetupTmpDir(0); +} diff --git a/libc/testlib/yield.c b/libc/testlib/yield.c index 4f889e3e9..c869caa85 100644 --- a/libc/testlib/yield.c +++ b/libc/testlib/yield.c @@ -18,7 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/testlib/ezbench.h" +#include "libc/thread/thread.h" void __testlib_yield(void) { - sched_yield(); + pthread_yield(); } diff --git a/libc/calls/alarm.c b/libc/thread/alarm.c similarity index 100% rename from libc/calls/alarm.c rename to libc/thread/alarm.c diff --git a/libc/calls/getitimer.c b/libc/thread/getitimer.c similarity index 100% rename from libc/calls/getitimer.c rename to libc/thread/getitimer.c diff --git a/libc/calls/setitimer-nt.c b/libc/thread/itimer.c similarity index 57% rename from libc/calls/setitimer-nt.c rename to libc/thread/itimer.c index 2eae3e706..f771a0acb 100644 --- a/libc/calls/setitimer-nt.c +++ b/libc/thread/itimer.c @@ -16,42 +16,80 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/sysv/consts/itimer.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/itimerval.h" +#include "libc/calls/struct/itimerval.internal.h" +#include "libc/calls/struct/timeval.h" +#include "libc/cosmo.h" +#include "libc/nt/enum/processcreationflags.h" +#include "libc/nt/thread.h" #include "libc/str/str.h" -#include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/itimer.internal.h" +#include "libc/thread/tls.h" +#include "third_party/nsync/mu.h" + #ifdef __x86_64__ -static struct itimerval g_setitimer; +struct IntervalTimer __itimer; -textwindows void _check_sigalrm(void) { - struct timeval now; - if (timeval_iszero(g_setitimer.it_value)) return; - now = timeval_real(); - if (timeval_cmp(now, g_setitimer.it_value) < 0) return; - if (timeval_iszero(g_setitimer.it_interval)) { - g_setitimer.it_value = timeval_zero; - } else { - do { - g_setitimer.it_value = - timeval_add(g_setitimer.it_value, g_setitimer.it_interval); - } while (timeval_cmp(now, g_setitimer.it_value) > 0); +static textwindows dontinstrument uint32_t __itimer_worker(void *arg) { + struct CosmoTib tls; + __bootstrap_tls(&tls, __builtin_frame_address(0)); + for (;;) { + bool dosignal = false; + struct timeval now, waituntil; + nsync_mu_lock(&__itimer.lock); + now = timeval_real(); + if (timeval_iszero(__itimer.it.it_value)) { + waituntil = timeval_max; + } else { + if (timeval_cmp(now, __itimer.it.it_value) < 0) { + waituntil = __itimer.it.it_value; + } else { + if (timeval_iszero(__itimer.it.it_interval)) { + __itimer.it.it_value = timeval_zero; + waituntil = timeval_max; + } else { + do { + __itimer.it.it_value = + timeval_add(__itimer.it.it_value, __itimer.it.it_interval); + } while (timeval_cmp(now, __itimer.it.it_value) > 0); + waituntil = __itimer.it.it_value; + } + dosignal = true; + } + } + nsync_mu_unlock(&__itimer.lock); + if (dosignal) { + __sig_generate(SIGALRM, SI_TIMER); + } + nsync_mu_lock(&__itimer.lock); + nsync_cv_wait_with_deadline(&__itimer.cond, &__itimer.lock, + timeval_totimespec(waituntil), 0); + nsync_mu_unlock(&__itimer.lock); } - __sig.pending |= 1ull << (SIGALRM - 1); + return 0; } -textwindows void sys_setitimer_nt_reset(void) { +static textwindows void __itimer_setup(void) { + __itimer.thread = CreateThread(0, 65536, __itimer_worker, 0, + kNtStackSizeParamIsAReservation, 0); +} + +textwindows void __itimer_reset(void) { // this function is called by fork(), because // timers aren't inherited by forked subprocesses - bzero(&g_setitimer, sizeof(g_setitimer)); + bzero(&__itimer, sizeof(__itimer)); } textwindows int sys_setitimer_nt(int which, const struct itimerval *neu, struct itimerval *old) { struct itimerval config; + cosmo_once(&__itimer.once, __itimer_setup); if (which != ITIMER_REAL || (neu && (!timeval_isvalid(neu->it_value) || !timeval_isvalid(neu->it_interval)))) { return einval(); @@ -61,16 +99,19 @@ textwindows int sys_setitimer_nt(int which, const struct itimerval *neu, // accommodate the usage setitimer(ITIMER_REAL, &it, &it) anyway config = *neu; } + nsync_mu_lock(&__itimer.lock); if (old) { - old->it_interval = g_setitimer.it_interval; - old->it_value = timeval_subz(g_setitimer.it_value, timeval_real()); + old->it_interval = __itimer.it.it_interval; + old->it_value = timeval_subz(__itimer.it.it_value, timeval_real()); } if (neu) { if (!timeval_iszero(config.it_value)) { config.it_value = timeval_add(config.it_value, timeval_real()); } - g_setitimer = config; + __itimer.it = config; + nsync_cv_signal(&__itimer.cond); } + nsync_mu_unlock(&__itimer.lock); return 0; } diff --git a/libc/thread/itimer.internal.h b/libc/thread/itimer.internal.h new file mode 100644 index 000000000..666d3058b --- /dev/null +++ b/libc/thread/itimer.internal.h @@ -0,0 +1,24 @@ +#ifndef COSMOPOLITAN_LIBC_ITIMER_H_ +#define COSMOPOLITAN_LIBC_ITIMER_H_ +#include "libc/atomic.h" +#include "libc/calls/struct/itimerval.h" +#include "third_party/nsync/cv.h" +#include "third_party/nsync/mu.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct IntervalTimer { + atomic_uint once; + intptr_t thread; + nsync_mu lock; + nsync_cv cond; + struct itimerval it; +}; + +extern struct IntervalTimer __itimer; + +void __itimer_reset(void); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_ITIMER_H_ */ diff --git a/libc/thread/mktls.c b/libc/thread/mktls.c index 5839d1502..c33930955 100644 --- a/libc/thread/mktls.c +++ b/libc/thread/mktls.c @@ -27,7 +27,6 @@ #include "libc/runtime/runtime.h" #include "libc/str/locale.h" #include "libc/str/str.h" -#include "libc/thread/spawn.h" #include "libc/thread/tls.h" #define I(x) ((uintptr_t)x) @@ -132,7 +131,6 @@ static char *_mktls_above(struct CosmoTib **out_tib) { * @return buffer that must be released with free() */ char *_mktls(struct CosmoTib **out_tib) { - __require_tls(); #ifdef __x86_64__ return _mktls_below(out_tib); #else diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 418887978..38f44f3fb 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -2,7 +2,6 @@ #define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigaltstack.h" -#include "libc/calls/struct/sigset.h" #include "libc/intrin/dll.h" #include "libc/runtime/runtime.h" #include "libc/thread/thread.h" @@ -14,9 +13,9 @@ #define PT_NOCANCEL 8 #define PT_MASKED 16 #define PT_INCANCEL 32 -#define PT_BLOCKED 64 -#define PT_EXITING 128 -#define PT_OPENBSD_KLUDGE 256 +#define PT_POLLING 64 // windows only +#define PT_INSEMAPHORE 128 // windows only +#define PT_OPENBSD_KLUDGE 128 // openbsd only #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -36,6 +35,9 @@ enum PosixThreadStatus { // // - kPosixThreadJoinable -> kPosixThreadDetached if pthread_detach() // is called on this thread. + // + // - kPosixThreadJoinable -> kPosixThreadZombie if another thread + // calls fork(). kPosixThreadJoinable, // this is a managed thread that'll be cleaned up by the library. @@ -45,6 +47,9 @@ enum PosixThreadStatus { // - kPosixThreadDetached -> kPosixThreadZombie if start_routine() // returns, or is longjmp'd out of by pthread_exit(), and the thread // is waiting to be joined. + // + // - kPosixThreadDetached -> kPosixThreadZombie if another thread + // calls fork(). kPosixThreadDetached, // this is a joinable thread that terminated. @@ -55,6 +60,8 @@ enum PosixThreadStatus { // pthread_join() is called by the user. // - kPosixThreadTerminated -> kPosixThreadZombie will happen when // pthread_detach() is called by the user. + // - kPosixThreadTerminated -> kPosixThreadZombie if another thread + // calls fork(). kPosixThreadTerminated, // this is a detached thread that terminated. @@ -69,7 +76,7 @@ enum PosixThreadStatus { #define POSIXTHREAD_CONTAINER(e) DLL_CONTAINER(struct PosixThread, list, e) struct PosixThread { - int flags; // 0x00: see PT_* constants + int pt_flags; // 0x00: see PT_* constants _Atomic(int) cancelled; // 0x04: thread has bad beliefs _Atomic(enum PosixThreadStatus) status; _Atomic(int) ptid; // transitions 0 → tid @@ -79,9 +86,13 @@ struct PosixThread { char *tls; // bottom of tls allocation struct CosmoTib *tib; // middle of tls allocation struct Dll list; // list of threads - pthread_t next; // for xnu silicon - jmp_buf exiter; // for pthread_exit + _Atomic(_Atomic(int) *) pt_futex; + intptr_t semaphore; + intptr_t iohandle; + void *ioverlap; + jmp_buf exiter; pthread_attr_t attr; + int abort_errno; struct _pthread_cleanup_buffer *cleanup; }; @@ -89,20 +100,29 @@ typedef void (*atfork_f)(void); extern struct Dll *_pthread_list; extern pthread_spinlock_t _pthread_lock; +extern struct PosixThread _pthread_static; extern _Atomic(pthread_key_dtor) _pthread_key_dtor[PTHREAD_KEYS_MAX]; -int _pthread_atfork(atfork_f, atfork_f, atfork_f); +void _pthread_decimate(void); +int _pthread_tid(struct PosixThread *); +void _pthread_unkey(struct CosmoTib *); +void _pthread_unwind(struct PosixThread *); int _pthread_reschedule(struct PosixThread *); +intptr_t _pthread_syshand(struct PosixThread *); +int _pthread_atfork(atfork_f, atfork_f, atfork_f); int _pthread_setschedparam_freebsd(int, int, const struct sched_param *); -int _pthread_signal(struct PosixThread *, int, int); +void _pthread_free(struct PosixThread *, bool); void _pthread_zombify(struct PosixThread *); -void _pthread_free(struct PosixThread *); void _pthread_onfork_prepare(void); void _pthread_onfork_parent(void); void _pthread_onfork_child(void); -long _pthread_cancel_sys(void); +long _pthread_cancel_ack(void); void _pthread_ungarbage(void); +__funline pureconst struct PosixThread *_pthread_self(void) { + return (struct PosixThread *)__get_tls()->tib_pthread; +} + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ */ diff --git a/libc/thread/pthread_atfork.c b/libc/thread/pthread_atfork.c index 4ce0519fb..b8b081a30 100644 --- a/libc/thread/pthread_atfork.c +++ b/libc/thread/pthread_atfork.c @@ -26,7 +26,9 @@ #include "libc/intrin/dll.h" #include "libc/intrin/handlock.internal.h" #include "libc/intrin/leaky.internal.h" +#include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -34,22 +36,18 @@ #include "libc/thread/thread.h" #include "libc/thread/tls.h" +struct AtFork { + struct AtFork *p[2]; + atfork_f f[3]; +}; + static struct AtForks { pthread_spinlock_t lock; - struct AtFork { - struct AtFork *p[2]; - atfork_f f[3]; - } * list; + struct AtFork *list; + struct AtFork pool[8]; + atomic_int allocated; } _atforks; -static void _pthread_purge(void) { - struct Dll *e; - while ((e = dll_first(_pthread_list))) { - dll_remove(&_pthread_list, e); - _pthread_free(POSIXTHREAD_CONTAINER(e)); - } -} - static void _pthread_onfork(int i) { struct AtFork *a; unassert(0 <= i && i <= 2); @@ -65,52 +63,48 @@ void _pthread_onfork_prepare(void) { _pthread_onfork(0); pthread_spin_lock(&_pthread_lock); __fds_lock(); - __hand_lock(); + if (IsWindows()) { + __hand_lock(); + } __mmi_lock(); } void _pthread_onfork_parent(void) { __mmi_unlock(); - __hand_unlock(); + if (IsWindows()) { + __hand_unlock(); + } __fds_unlock(); pthread_spin_unlock(&_pthread_lock); _pthread_onfork(1); } void _pthread_onfork_child(void) { - struct CosmoTib *tib; - struct PosixThread *pt; + if (IsWindows()) __hand_wipe(); pthread_mutexattr_t attr; - extern pthread_mutex_t __mmi_lock_obj; - tib = __get_tls(); - pt = (struct PosixThread *)tib->tib_pthread; - - // let's choose to let the new process live. - // even though it's unclear what to do with this kind of race. - atomic_store_explicit(&pt->cancelled, false, memory_order_relaxed); - - // wipe core runtime locks - __hand_init(); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + extern pthread_mutex_t __mmi_lock_obj; pthread_mutex_init(&__mmi_lock_obj, &attr); pthread_mutex_init(&__fds_lock_obj, &attr); (void)pthread_spin_init(&_pthread_lock, 0); - - // call user-supplied forked child callbacks _pthread_onfork(2); +} - // delete other threads that existed before forking - // this must come after onfork, since it calls free - dll_remove(&_pthread_list, &pt->list); - _pthread_purge(); - dll_make_first(&_pthread_list, &pt->list); +static struct AtFork *_pthread_atfork_alloc(void) { + int i, n = ARRAYLEN(_atforks.pool); + if (atomic_load_explicit(&_atforks.allocated, memory_order_relaxed) < n && + (i = atomic_fetch_add(&_atforks.allocated, 1)) < n) { + return _atforks.pool + i; + } else { + return malloc(sizeof(struct AtFork)); + } } int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) { int rc; struct AtFork *a; - if (!(a = malloc(sizeof(struct AtFork)))) return ENOMEM; + if (!(a = _pthread_atfork_alloc())) return ENOMEM; a->f[0] = prepare; a->f[1] = parent; a->f[2] = child; diff --git a/libc/thread/pthread_attr_setguardsize.c b/libc/thread/pthread_attr_setguardsize.c index d6f79b248..ace014d02 100644 --- a/libc/thread/pthread_attr_setguardsize.c +++ b/libc/thread/pthread_attr_setguardsize.c @@ -16,15 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/thread/thread.h" /** * Sets size of unmapped pages at bottom of stack. * + * This value will be rounded up to the host microprocessor page size, + * which is usually 4096 or 16384. It's important to write code in such + * a way that that code can't skip over the guard area. GCC has warnings + * like `-Wframe-larger-than=4096 -Walloca-larger-than=4096` which help + * guarantee your code is safe in this regard. It should be assumed the + * guard pages exist beneath the stack pointer, rather than the bottom + * of the stack, since guard pages are also used to grow down commit, + * which can be poked using CheckLargeStackAllocation(). + * * @param guardsize contains guard size in bytes * @return 0 on success, or errno on error + * @raise EINVAL if `guardsize` is zero */ errno_t pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { + if (!guardsize) return EINVAL; attr->__guardsize = guardsize; return 0; } diff --git a/libc/thread/pthread_attr_setinheritsched.c b/libc/thread/pthread_attr_setinheritsched.c index 0978d33cb..265f725f3 100644 --- a/libc/thread/pthread_attr_setinheritsched.c +++ b/libc/thread/pthread_attr_setinheritsched.c @@ -20,6 +20,8 @@ #include "libc/errno.h" #include "libc/thread/thread.h" +__static_yoink("_pthread_reschedule"); + /** * Sets thread scheduler inheritance attribute, e.g. * diff --git a/libc/thread/pthread_cancel.c b/libc/thread/pthread_cancel.c index 07dbe8ab5..411380e79 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/thread/pthread_cancel.c @@ -18,19 +18,29 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" +#include "libc/nt/enum/context.h" +#include "libc/nt/enum/threadaccess.h" +#include "libc/nt/runtime.h" +#include "libc/nt/struct/context.h" +#include "libc/nt/thread.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" @@ -42,42 +52,135 @@ int systemfive_cancel(void); extern const char systemfive_cancellable[]; extern const char systemfive_cancellable_end[]; -long _pthread_cancel_sys(void) { - struct PosixThread *pt; - pt = (struct PosixThread *)__get_tls()->tib_pthread; - if (!(pt->flags & (PT_NOCANCEL | PT_MASKED)) || (pt->flags & PT_ASYNC)) { +long _pthread_cancel_ack(void) { + struct PosixThread *pt = _pthread_self(); + if (!(pt->pt_flags & (PT_NOCANCEL | PT_MASKED)) || + (pt->pt_flags & PT_ASYNC)) { pthread_exit(PTHREAD_CANCELED); } - pt->flags |= PT_NOCANCEL | PT_OPENBSD_KLUDGE; + pt->pt_flags |= PT_NOCANCEL | PT_OPENBSD_KLUDGE; return ecanceled(); } -static void OnSigThr(int sig, siginfo_t *si, void *ctx) { - ucontext_t *uc = ctx; - struct CosmoTib *t; +static void _pthread_cancel_sig(int sig, siginfo_t *si, void *arg) { + ucontext_t *ctx = arg; + + // check thread runtime state is initialized and cancelled struct PosixThread *pt; - if ((t = __get_tls()) && // TODO: why can it be null on freebsd? - (pt = (struct PosixThread *)t->tib_pthread) && - !(pt->flags & PT_NOCANCEL) && - atomic_load_explicit(&pt->cancelled, memory_order_acquire)) { - sigaddset(&uc->uc_sigmask, sig); - if (systemfive_cancellable <= (char *)uc->uc_mcontext.PC && - (char *)uc->uc_mcontext.PC < systemfive_cancellable_end) { - uc->uc_mcontext.PC = (intptr_t)systemfive_cancel; - } else if (pt->flags & PT_ASYNC) { - pthread_exit(PTHREAD_CANCELED); - } else { - __tkill(atomic_load_explicit(&t->tib_tid, memory_order_relaxed), sig, t); + if (!__tls_enabled) return; + if (!(pt = _pthread_self())) return; + if (pt->pt_flags & PT_NOCANCEL) return; + if (!atomic_load_explicit(&pt->cancelled, memory_order_acquire)) return; + + // in asynchronous mode we'll just the exit asynchronously + if (pt->pt_flags & PT_ASYNC) { + sigaddset(&ctx->uc_sigmask, SIGTHR); + pthread_sigmask(SIG_SETMASK, &ctx->uc_sigmask, 0); + pthread_exit(PTHREAD_CANCELED); + } + + // prevent this handler from being called again by thread + sigaddset(&ctx->uc_sigmask, SIGTHR); + + // check for race condition between pre-check and syscall + // rewrite the thread's execution state to acknowledge it + if (systemfive_cancellable <= (char *)ctx->uc_mcontext.PC && + (char *)ctx->uc_mcontext.PC < systemfive_cancellable_end) { + ctx->uc_mcontext.PC = (intptr_t)systemfive_cancel; + return; + } + + // punts cancellation to start of next cancellation point + // we ensure sigthr is a pending signal in case unblocked + if (IsXnuSilicon()) { + __syslib->__pthread_kill(_pthread_syshand(pt), sig); + } else { + sys_tkill(_pthread_tid(pt), sig, __get_tls()); + } +} + +static void _pthread_cancel_listen(void) { + struct sigaction sa; + if (!IsWindows()) { + sa.sa_sigaction = _pthread_cancel_sig; + sa.sa_flags = SA_SIGINFO | SA_RESTART; + memset(&sa.sa_mask, -1, sizeof(sa.sa_mask)); + npassert(!sigaction(SIGTHR, &sa, 0)); + } +} + +static void pthread_cancel_nt(struct PosixThread *pt, intptr_t hThread) { + uint32_t old_suspend_count; + if ((pt->pt_flags & PT_ASYNC) && !(pt->pt_flags & PT_NOCANCEL)) { + if ((old_suspend_count = SuspendThread(hThread)) != -1u) { + if (!old_suspend_count) { + struct NtContext cpu; + cpu.ContextFlags = kNtContextControl | kNtContextInteger; + if (GetThreadContext(hThread, &cpu)) { + pt->pt_flags |= PT_NOCANCEL; + cpu.Rip = (uintptr_t)pthread_exit; + cpu.Rdi = (uintptr_t)PTHREAD_CANCELED; + cpu.Rsp &= -16; + *(uintptr_t *)(cpu.Rsp -= sizeof(uintptr_t)) = cpu.Rip; + unassert(SetThreadContext(hThread, &cpu)); + __sig_cancel(pt, 0); + } + } + ResumeThread(hThread); } } } -static void ListenForSigThr(void) { - struct sigaction sa; - sa.sa_sigaction = OnSigThr; - sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK; - memset(&sa.sa_mask, -1, sizeof(sa.sa_mask)); - npassert(!sigaction(SIGTHR, &sa, 0)); +static errno_t _pthread_cancel_impl(struct PosixThread *pt) { + + // install our special signal handler + static bool once; + if (!once) { + _pthread_cancel_listen(); + once = true; + } + + // check if thread is already dead + switch (atomic_load_explicit(&pt->status, memory_order_acquire)) { + case kPosixThreadZombie: + case kPosixThreadTerminated: + return ESRCH; + default: + break; + } + + // flip the bit indicating that this thread is cancelled + atomic_store_explicit(&pt->cancelled, 1, memory_order_release); + + // does this thread want to cancel itself? + if (pt == _pthread_self()) { + unassert(!(pt->pt_flags & PT_NOCANCEL)); + if (!(pt->pt_flags & (PT_NOCANCEL | PT_MASKED)) && + (pt->pt_flags & PT_ASYNC)) { + pthread_exit(PTHREAD_CANCELED); + } + return 0; + } + + errno_t err; + if (IsWindows()) { + pthread_cancel_nt(pt, _pthread_syshand(pt)); + err = 0; + } else if (IsXnuSilicon()) { + err = __syslib->__pthread_kill(_pthread_syshand(pt), SIGTHR); + } else { + int e = errno; + if (!sys_tkill(_pthread_tid(pt), SIGTHR, pt->tib)) { + err = 0; + } else { + err = errno; + errno = e; + } + } + if (err == ESRCH) { + err = 0; // we already reported this + } + return err; } /** @@ -153,6 +256,7 @@ static void ListenForSigThr(void) { * - `nsync_cv_wait_with_deadline` * - `nsync_cv_wait` * - `opendir` + * - `openatemp`, 'mkstemp', etc. * - `pclose` * - `popen` * - `fwrite`, `printf`, `fprintf`, `putc`, etc. @@ -174,7 +278,7 @@ static void ListenForSigThr(void) { * - `INFOF()`, `WARNF()`, etc. * - `getentropy` * - `gmtime_r` - * - `kprintf` + * - `kprintf` (by virtue of asm(syscall) and write_nocancel() on xnu) * - `localtime_r` * - `nsync_mu_lock` * - `nsync_mu_unlock` @@ -185,6 +289,7 @@ static void ListenForSigThr(void) { * - `pthread_setname_np` * - `sem_open` * - `system` + * - `openatemp`, 'mkstemp', etc. * - `timespec_sleep` * - `touch` * @@ -254,53 +359,38 @@ static void ListenForSigThr(void) { * * Isn't safe to use in masked mode. That's because if a cancellation * occurs during the write() operation then cancellations are blocked - * while running read(). Masked mode doesn't have second chances. You + * while running read(). MASKED MODE DOESN'T HAVE SECOND CHANCES. You * must rigorously check the results of each cancellation point call. * * Unit tests should be able to safely ignore the return value, or at * the very least be programmed to consider ESRCH a successful status * + * @param thread may be 0 to cancel all threads except self * @return 0 on success, or errno on error * @raise ESRCH if system thread wasn't alive or we lost a race */ errno_t pthread_cancel(pthread_t thread) { - int e, rc, tid; - static bool once; - struct PosixThread *pt; - __require_tls(); - if (!once) { - ListenForSigThr(); - once = true; - } - pt = (struct PosixThread *)thread; - switch (atomic_load_explicit(&pt->status, memory_order_acquire)) { - case kPosixThreadZombie: - case kPosixThreadTerminated: - return ESRCH; - default: - break; - } - atomic_store_explicit(&pt->cancelled, 1, memory_order_release); - if (thread == __get_tls()->tib_pthread) { - if (!(pt->flags & (PT_NOCANCEL | PT_MASKED)) && (pt->flags & PT_ASYNC)) { - pthread_exit(PTHREAD_CANCELED); - } - return 0; - } - if (!(rc = pthread_getunique_np(thread, &tid))) { - if (!IsWindows()) { - e = errno; - if (!__tkill(tid, SIGTHR, pt->tib)) { - rc = 0; - } else { - rc = errno; - errno = e; + errno_t err; + struct Dll *e; + struct PosixThread *arg, *other; + if ((arg = (struct PosixThread *)thread)) { + err = _pthread_cancel_impl(arg); + } else { + err = ESRCH; + pthread_spin_lock(&_pthread_lock); + for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { + other = POSIXTHREAD_CONTAINER(e); + if (other != _pthread_self() && + atomic_load_explicit(&other->status, memory_order_acquire) < + kPosixThreadTerminated) { + _pthread_cancel_impl(other); + err = 0; } - } else { - rc = 0; } + pthread_spin_unlock(&_pthread_lock); } - return rc; + STRACE("pthread_cancel(%d) → %s", _pthread_tid(arg), DescribeErrno(err)); + return err; } /** @@ -317,9 +407,9 @@ errno_t pthread_cancel(pthread_t thread) { void pthread_testcancel(void) { struct PosixThread *pt; if (!__tls_enabled) return; - if (!(pt = (struct PosixThread *)__get_tls()->tib_pthread)) return; - if (pt->flags & PT_NOCANCEL) return; - if ((!(pt->flags & PT_MASKED) || (pt->flags & PT_ASYNC)) && + if (!(pt = _pthread_self())) return; + if (pt->pt_flags & PT_NOCANCEL) return; + if ((!(pt->pt_flags & PT_MASKED) || (pt->pt_flags & PT_ASYNC)) && atomic_load_explicit(&pt->cancelled, memory_order_acquire)) { pthread_exit(PTHREAD_CANCELED); } @@ -344,13 +434,13 @@ void pthread_testcancel(void) { errno_t pthread_testcancel_np(void) { struct PosixThread *pt; if (!__tls_enabled) return 0; - if (!(pt = (struct PosixThread *)__get_tls()->tib_pthread)) return 0; - if (pt->flags & PT_NOCANCEL) return 0; + if (!(pt = _pthread_self())) return 0; + if (pt->pt_flags & PT_NOCANCEL) return 0; if (!atomic_load_explicit(&pt->cancelled, memory_order_acquire)) return 0; - if (!(pt->flags & PT_MASKED) || (pt->flags & PT_ASYNC)) { + if (!(pt->pt_flags & PT_MASKED) || (pt->pt_flags & PT_ASYNC)) { pthread_exit(PTHREAD_CANCELED); } else { - pt->flags |= PT_NOCANCEL; + pt->pt_flags |= PT_NOCANCEL; return ECANCELED; } } diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index c49daf5ad..2efd84fc7 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -20,20 +20,28 @@ #include "libc/atomic.h" #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" #include "libc/intrin/bsr.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/dll.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" +#include "libc/mem/alloca.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/crc32.h" +#include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" +#include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/clone.h" @@ -42,10 +50,8 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/ss.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" __static_yoink("nsync_mu_lock"); __static_yoink("nsync_mu_unlock"); @@ -56,39 +62,44 @@ __static_yoink("_pthread_atfork"); #define MAP_ANON_OPENBSD 0x1000 #define MAP_STACK_OPENBSD 0x4000 -static unsigned long roundup2pow(unsigned long x) { - return x > 1 ? 2ul << _bsrl(x - 1) : x ? 1 : 0; -} - -void _pthread_free(struct PosixThread *pt) { - if (pt->flags & PT_STATIC) return; - free(pt->tls); - if ((pt->flags & PT_OWNSTACK) && // - pt->attr.__stackaddr && // - pt->attr.__stackaddr != MAP_FAILED) { +void _pthread_free(struct PosixThread *pt, bool isfork) { + if (pt->pt_flags & PT_STATIC) return; + if (pt->pt_flags & PT_OWNSTACK) { unassert(!munmap(pt->attr.__stackaddr, pt->attr.__stacksize)); } + if (!isfork) { + if (IsWindows()) { + if (pt->tib->tib_syshand) { + unassert(CloseHandle(pt->tib->tib_syshand)); + } + } else if (IsXnuSilicon()) { + if (pt->tib->tib_syshand) { + __syslib->__pthread_join(pt->tib->tib_syshand, 0); + } + } + } + free(pt->tls); free(pt); } static int PosixThread(void *arg, int tid) { void *rc; struct PosixThread *pt = arg; - unassert(__get_tls()->tib_tid > 0); if (pt->attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) { - _pthread_reschedule(pt); + unassert(_weaken(_pthread_reschedule)); + _weaken(_pthread_reschedule)(pt); // yoinked by attribute builder } // set long jump handler so pthread_exit can bring control back here if (!setjmp(pt->exiter)) { - pt->next = __get_tls()->tib_pthread; - __get_tls()->tib_pthread = (pthread_t)pt; - unassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0)); + pthread_sigmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0); rc = pt->start(pt->arg); // ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup unassert(!pt->cleanup); // calling pthread_exit() will either jump back here, or call exit pthread_exit(rc); } + // avoid signal handler being triggered after we trash our own stack + _sigblockall(); // return to clone polyfill which clears tid, wakes futex, and exits return 0; } @@ -160,7 +171,7 @@ static errno_t pthread_create_impl(pthread_t *thread, // assume they know what they're doing as much as possible if (IsOpenbsd()) { if ((rc = FixupCustomStackOnOpenbsd(&pt->attr))) { - _pthread_free(pt); + _pthread_free(pt, false); return rc; } } @@ -168,23 +179,19 @@ static errno_t pthread_create_impl(pthread_t *thread, // cosmo is managing the stack // 1. in mono repo optimize for tiniest stack possible // 2. in public world optimize to *work* regardless of memory - unsigned long default_guardsize; - default_guardsize = getauxval(AT_PAGESZ); - pt->flags = PT_OWNSTACK; - pt->attr.__stacksize = MAX(pt->attr.__stacksize, GetStackSize()); - pt->attr.__stacksize = roundup2pow(pt->attr.__stacksize); - pt->attr.__guardsize = ROUNDUP(pt->attr.__guardsize, default_guardsize); - if (pt->attr.__guardsize + default_guardsize >= pt->attr.__stacksize) { - _pthread_free(pt); + int granularity = FRAMESIZE; + int pagesize = getauxval(AT_PAGESZ); + pt->attr.__guardsize = ROUNDUP(pt->attr.__guardsize, pagesize); + pt->attr.__stacksize = ROUNDUP(pt->attr.__stacksize, granularity); + if (pt->attr.__guardsize + pagesize > pt->attr.__stacksize) { + _pthread_free(pt, false); return EINVAL; } - if (pt->attr.__guardsize == default_guardsize) { - // user is wisely using smaller stacks with default guard size + if (pt->attr.__guardsize == pagesize) { pt->attr.__stackaddr = mmap(0, pt->attr.__stacksize, PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); } else { - // user is tuning things, performance may suffer pt->attr.__stackaddr = mmap(0, pt->attr.__stacksize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -203,9 +210,9 @@ static errno_t pthread_create_impl(pthread_t *thread, } } } - if (pt->attr.__stackaddr == MAP_FAILED) { + if (!pt->attr.__stackaddr || pt->attr.__stackaddr == MAP_FAILED) { rc = errno; - _pthread_free(pt); + _pthread_free(pt, false); errno = e; if (rc == EINVAL || rc == EOVERFLOW) { return EINVAL; @@ -213,6 +220,7 @@ static errno_t pthread_create_impl(pthread_t *thread, return EAGAIN; } } + pt->pt_flags |= PT_OWNSTACK; if (IsAsan() && pt->attr.__guardsize) { __asan_poison(pt->attr.__stackaddr, pt->attr.__guardsize, kAsanStackOverflow); @@ -220,6 +228,8 @@ static errno_t pthread_create_impl(pthread_t *thread, } // set initial status + pt->tib->tib_pthread = (pthread_t)pt; + atomic_store_explicit(&pt->tib->tib_sigmask, -1, memory_order_relaxed); if (!pt->attr.__havesigmask) { pt->attr.__havesigmask = true; memcpy(pt->attr.__sigmask, &oldsigs, sizeof(oldsigs)); @@ -234,15 +244,15 @@ static errno_t pthread_create_impl(pthread_t *thread, memory_order_relaxed); break; default: - _pthread_free(pt); + _pthread_free(pt, false); return EINVAL; } // add thread to global list - // we add it to the end since zombies go at the beginning + // we add it to the beginning since zombies go at the end dll_init(&pt->list); pthread_spin_lock(&_pthread_lock); - dll_make_last(&_pthread_list, &pt->list); + dll_make_first(&_pthread_list, &pt->list); pthread_spin_unlock(&_pthread_lock); // launch PosixThread(pt) in new thread @@ -256,7 +266,7 @@ static errno_t pthread_create_impl(pthread_t *thread, pthread_spin_lock(&_pthread_lock); dll_remove(&_pthread_list, &pt->list); pthread_spin_unlock(&_pthread_lock); - _pthread_free(pt); + _pthread_free(pt, false); return rc; } @@ -264,6 +274,13 @@ static errno_t pthread_create_impl(pthread_t *thread, return 0; } +static const char *DescribeHandle(char buf[12], errno_t err, pthread_t *th) { + if (err) return "n/a"; + if (!th) return "NULL"; + FormatInt32(buf, _pthread_tid((struct PosixThread *)*th)); + return buf; +} + /** * Creates thread, e.g. * @@ -316,11 +333,13 @@ static errno_t pthread_create_impl(pthread_t *thread, */ errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { - errno_t rc; - __require_tls(); - pthread_decimate_np(); + errno_t err; + _pthread_decimate(); BLOCK_SIGNALS; - rc = pthread_create_impl(thread, attr, start_routine, arg, _SigMask); + err = pthread_create_impl(thread, attr, start_routine, arg, _SigMask); ALLOW_SIGNALS; - return rc; + STRACE("pthread_create([%s], %p, %t, %p) → %s", + DescribeHandle(alloca(12), err, thread), attr, start_routine, arg, + DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_decimate_np.c b/libc/thread/pthread_decimate.c similarity index 95% rename from libc/thread/pthread_decimate_np.c rename to libc/thread/pthread_decimate.c index 4ba4c97a9..daa5fdfed 100644 --- a/libc/thread/pthread_decimate_np.c +++ b/libc/thread/pthread_decimate.c @@ -27,13 +27,13 @@ /** * Releases memory of detached threads that have terminated. */ -void pthread_decimate_np(void) { +void _pthread_decimate(void) { struct Dll *e; struct PosixThread *pt; enum PosixThreadStatus status; StartOver: pthread_spin_lock(&_pthread_lock); - for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { + for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) { pt = POSIXTHREAD_CONTAINER(e); if (pt->tib == __get_tls()) continue; status = atomic_load_explicit(&pt->status, memory_order_acquire); @@ -41,7 +41,7 @@ StartOver: if (!atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)) { dll_remove(&_pthread_list, e); pthread_spin_unlock(&_pthread_lock); - _pthread_free(pt); + _pthread_free(pt, false); goto StartOver; } } diff --git a/libc/thread/pthread_detach.c b/libc/thread/pthread_detach.c index 384d05d63..ac479c9ca 100644 --- a/libc/thread/pthread_detach.c +++ b/libc/thread/pthread_detach.c @@ -19,13 +19,36 @@ #include "libc/assert.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" +static errno_t pthread_detach_impl(struct PosixThread *pt) { + enum PosixThreadStatus status, transition; + for (;;) { + status = atomic_load_explicit(&pt->status, memory_order_acquire); + if (status == kPosixThreadJoinable) { + transition = kPosixThreadDetached; + } else if (status == kPosixThreadTerminated) { + transition = kPosixThreadZombie; + } else { + return EINVAL; + } + if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition, + memory_order_release, + memory_order_relaxed)) { + if (transition == kPosixThreadZombie) { + _pthread_zombify(pt); + } + _pthread_decimate(); + return 0; + } + } +} + /** * Asks POSIX thread to free itself automatically upon termination. * @@ -37,30 +60,13 @@ * pthread_detach() can't be called twice on the same thread. * * @return 0 on success, or errno with error + * @raise EINVAL if `thread` isn't joinable * @returnserrno * @threadsafe */ errno_t pthread_detach(pthread_t thread) { - struct PosixThread *pt; - enum PosixThreadStatus status, transition; - for (pt = (struct PosixThread *)thread;;) { - status = atomic_load_explicit(&pt->status, memory_order_acquire); - if (status == kPosixThreadJoinable) { - transition = kPosixThreadDetached; - } else if (status == kPosixThreadTerminated) { - transition = kPosixThreadZombie; - } else { - __builtin_unreachable(); - } - if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition, - memory_order_release, - memory_order_relaxed)) { - break; - } - } - if (transition == kPosixThreadZombie) { - _pthread_zombify(pt); - } - pthread_decimate_np(); - return 0; + struct PosixThread *pt = (struct PosixThread *)thread; + errno_t err = pthread_detach_impl(pt); + STRACE("pthread_detach(%d) → %s", _pthread_tid(pt), DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_exit.c b/libc/thread/pthread_exit.c index d7da9949e..62088a458 100644 --- a/libc/thread/pthread_exit.c +++ b/libc/thread/pthread_exit.c @@ -24,13 +24,15 @@ #include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/mem/gc.h" +#include "libc/mem/mem.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" #include "third_party/nsync/futex.internal.h" -static void CleanupThread(struct PosixThread *pt) { +void _pthread_unwind(struct PosixThread *pt) { struct _pthread_cleanup_buffer *cb; while ((cb = pt->cleanup)) { pt->cleanup = cb->__prev; @@ -38,25 +40,27 @@ static void CleanupThread(struct PosixThread *pt) { } } -static void DestroyTlsKeys(struct CosmoTib *tib) { +void _pthread_unkey(struct CosmoTib *tib) { int i, j, gotsome; void *val, **keys; pthread_key_dtor dtor; - keys = tib->tib_keys; - for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) { - for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) { - if ((val = keys[i]) && - (dtor = atomic_load_explicit(_pthread_key_dtor + i, - memory_order_relaxed)) && - dtor != (pthread_key_dtor)-1) { - gotsome = 1; - keys[i] = 0; - dtor(val); + if ((keys = tib->tib_keys)) { + for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) { + for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) { + if ((val = keys[i]) && + (dtor = atomic_load_explicit(_pthread_key_dtor + i, + memory_order_relaxed)) && + dtor != (pthread_key_dtor)-1) { + gotsome = 1; + keys[i] = 0; + dtor(val); + } + } + if (!gotsome) { + break; } } - if (!gotsome) { - break; - } + free(keys); } } @@ -98,19 +102,25 @@ wontreturn void pthread_exit(void *rc) { struct PosixThread *pt; enum PosixThreadStatus status, transition; - STRACE("pthread_exit(%p)", rc); - tib = __get_tls(); pt = (struct PosixThread *)tib->tib_pthread; - unassert(~pt->flags & PT_EXITING); - pt->flags |= PT_EXITING; + pt->pt_flags |= PT_NOCANCEL; pt->rc = rc; + STRACE("pthread_exit(%p)", rc); + // free resources - CleanupThread(pt); - DestroyTlsKeys(tib); + _pthread_unwind(pt); + _pthread_unkey(tib); _pthread_ungarbage(); - pthread_decimate_np(); + _pthread_decimate(); + + // run atexit handlers if orphaned thread + if (pthread_orphan_np()) { + if (_weaken(__cxa_finalize)) { + _weaken(__cxa_finalize)(NULL); + } + } // transition the thread to a terminated state status = atomic_load_explicit(&pt->status, memory_order_acquire); @@ -134,14 +144,17 @@ wontreturn void pthread_exit(void *rc) { _pthread_zombify(pt); } - // check if this is the main thread or an orphaned thread + // check if this is the last survivor if (pthread_orphan_np()) { - exit(0); + for (const uintptr_t *p = __fini_array_end; p > __fini_array_start;) { + ((void (*)(void))(*--p))(); + } + _Exit(0); } // check if the main thread has died whilst children live // note that the main thread is joinable by child threads - if (pt->flags & PT_STATIC) { + if (pt->pt_flags & PT_STATIC) { atomic_store_explicit(&tib->tib_tid, 0, memory_order_release); nsync_futex_wake_(&tib->tib_tid, INT_MAX, !IsWindows()); _Exit1(0); diff --git a/libc/thread/pthread_getaffinity_np.c b/libc/thread/pthread_getaffinity_np.c index 1e5bda165..aef970a5d 100644 --- a/libc/thread/pthread_getaffinity_np.c +++ b/libc/thread/pthread_getaffinity_np.c @@ -39,34 +39,33 @@ errno_t pthread_getaffinity_np(pthread_t thread, size_t size, cpu_set_t *bitset) { int rc, tid; + tid = _pthread_tid((struct PosixThread *)thread); - if (!(rc = pthread_getunique_np(thread, &tid))) { - if (size != sizeof(cpu_set_t)) { - rc = einval(); - } else if (IsWindows() || IsMetal() || IsOpenbsd()) { - rc = enosys(); - } else if (IsFreebsd()) { - if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, - 32, bitset)) { - rc = 32; - } else { - rc = -1; - } - } else if (IsNetbsd()) { - if (!sys_sched_getaffinity_netbsd(tid, 0, 32, bitset)) { - rc = 32; - } else { - rc = -1; - } + if (size != sizeof(cpu_set_t)) { + rc = einval(); + } else if (IsWindows() || IsMetal() || IsOpenbsd()) { + rc = enosys(); + } else if (IsFreebsd()) { + if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, + bitset)) { + rc = 32; } else { - rc = sys_sched_getaffinity(tid, size, bitset); + rc = -1; } - if (rc > 0) { - if (rc < size) { - bzero((char *)bitset + rc, size - rc); - } - rc = 0; + } else if (IsNetbsd()) { + if (!sys_sched_getaffinity_netbsd(tid, 0, 32, bitset)) { + rc = 32; + } else { + rc = -1; } + } else { + rc = sys_sched_getaffinity(tid, size, bitset); + } + if (rc > 0) { + if (rc < size) { + bzero((char *)bitset + rc, size - rc); + } + rc = 0; } STRACE("pthread_getaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset, diff --git a/libc/thread/pthread_getattr_np.c b/libc/thread/pthread_getattr_np.c index c24ca7756..497966839 100644 --- a/libc/thread/pthread_getattr_np.c +++ b/libc/thread/pthread_getattr_np.c @@ -16,8 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/dce.h" #include "libc/intrin/atomic.h" +#include "libc/limits.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/rlim.h" +#include "libc/sysv/consts/rlimit.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" @@ -37,7 +47,7 @@ * thread. This is useful for knowing where the stack is. It can also * be useful If you explicitly configured a stack too, since we might * have needed to slightly tune the address and size to meet platform - * requirements. This function returns information that reflects that + * requirements. * * 3. You can view changes pthread_create() may have made to the stack * guard size by calling pthread_attr_getguardsize() on `attr` @@ -62,5 +72,9 @@ errno_t pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) { default: __builtin_unreachable(); } + if (!attr->__stacksize && (pt->pt_flags & PT_STATIC)) { + __get_main_stack(&attr->__stackaddr, &attr->__stacksize, + &attr->__guardsize); + } return 0; } diff --git a/libc/thread/pthread_getname_np.c b/libc/thread/pthread_getname_np.c index f9c9f409b..12e16a6d9 100644 --- a/libc/thread/pthread_getname_np.c +++ b/libc/thread/pthread_getname_np.c @@ -31,10 +31,11 @@ #include "libc/sysv/consts/pr.h" #include "libc/thread/posixthread.internal.h" -static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) { +static errno_t pthread_getname_impl(struct PosixThread *pt, char *name, + size_t size) { int e, fd, rc, tid, len; - if ((rc = pthread_getunique_np(thread, &tid))) return rc; + tid = _pthread_tid(pt); if (!size) return 0; bzero(name, size); e = errno; @@ -127,8 +128,10 @@ static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) { */ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) { errno_t rc; + struct PosixThread *pt; + pt = (struct PosixThread *)thread; BLOCK_CANCELLATIONS; - rc = pthread_getname_impl(thread, name, size); + rc = pthread_getname_impl(pt, name, size); ALLOW_CANCELLATIONS; return rc; } diff --git a/libc/intrin/pthread_setspecific.c b/libc/thread/pthread_getspecific.c similarity index 73% rename from libc/intrin/pthread_setspecific.c rename to libc/thread/pthread_getspecific.c index dbd847bc3..82ccbaffc 100644 --- a/libc/intrin/pthread_setspecific.c +++ b/libc/thread/pthread_getspecific.c @@ -18,10 +18,18 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/intrin/atomic.h" +#include "libc/mem/mem.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" +// this is a legacy api so we avoid making the tib 1024 bytes larger +static void pthread_key_init(void) { + if (!__get_tls()->tib_keys) { + __get_tls()->tib_keys = calloc(PTHREAD_KEYS_MAX, sizeof(void *)); + } +} + /** * Sets value of TLS slot for current thread. * @@ -35,8 +43,28 @@ int pthread_setspecific(pthread_key_t k, const void *val) { // pthread_key_create() or after key has been deleted with // pthread_key_delete() is undefined." // ──Quoth POSIX.1-2017 + pthread_key_init(); unassert(0 <= k && k < PTHREAD_KEYS_MAX); unassert(atomic_load_explicit(_pthread_key_dtor + k, memory_order_acquire)); __get_tls()->tib_keys[k] = (void *)val; return 0; } + +/** + * Gets value of TLS slot for current thread. + * + * If `k` wasn't created by pthread_key_create() then the behavior is + * undefined. If `k` was unregistered earlier by pthread_key_delete() + * then the behavior is undefined. + */ +void *pthread_getspecific(pthread_key_t k) { + // "The effect of calling pthread_getspecific() or + // pthread_setspecific() with a key value not obtained from + // pthread_key_create() or after key has been deleted with + // pthread_key_delete() is undefined." + // ──Quoth POSIX.1-2017 + pthread_key_init(); + unassert(0 <= k && k < PTHREAD_KEYS_MAX); + unassert(atomic_load_explicit(_pthread_key_dtor + k, memory_order_acquire)); + return __get_tls()->tib_keys[k]; +} diff --git a/libc/thread/pthread_getunique_np.c b/libc/thread/pthread_getunique_np.c index 753d66eb7..bed673c71 100644 --- a/libc/thread/pthread_getunique_np.c +++ b/libc/thread/pthread_getunique_np.c @@ -16,26 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/intrin/atomic.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" /** * Returns system thread id of POSIX thread. - * * @return 0 on success, or errno on error */ errno_t pthread_getunique_np(pthread_t thread, pthread_id_np_t *out_tid) { - int tid; - struct PosixThread *pt; - for (pt = (struct PosixThread *)thread;;) { - tid = atomic_load_explicit(&pt->ptid, memory_order_acquire); - if (!tid) { - pthread_yield(); - } else { - *out_tid = tid; - return 0; - } - } + *out_tid = _pthread_tid((struct PosixThread *)thread); + return 0; } diff --git a/libc/intrin/pthread_key_create.c b/libc/thread/pthread_key_create.c similarity index 100% rename from libc/intrin/pthread_key_create.c rename to libc/thread/pthread_key_create.c diff --git a/libc/intrin/pthread_key_delete.c b/libc/thread/pthread_key_delete.c similarity index 100% rename from libc/intrin/pthread_key_delete.c rename to libc/thread/pthread_key_delete.c diff --git a/libc/intrin/pthread_keys.c b/libc/thread/pthread_keys.c similarity index 100% rename from libc/intrin/pthread_keys.c rename to libc/thread/pthread_keys.c diff --git a/libc/thread/pthread_kill.c b/libc/thread/pthread_kill.c index f4390cede..f9695adc9 100644 --- a/libc/thread/pthread_kill.c +++ b/libc/thread/pthread_kill.c @@ -17,9 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/syscall_support-sysv.internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" +#include "libc/runtime/syslib.internal.h" +#include "libc/sysv/consts/sicode.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" @@ -29,22 +35,38 @@ * @return 0 on success, or errno on error * @raise ESRCH if `tid` was valid but no such thread existed * @raise EAGAIN if `RLIMIT_SIGPENDING` was exceeded - * @raise EINVAL if `tid` or `sig` was invalid + * @raise EINVAL if `sig` wasn't a legal signal * @raise EPERM if permission was denied * @asyncsignalsafe */ errno_t pthread_kill(pthread_t thread, int sig) { - int e, rc, tid; - struct PosixThread *p; - if (!(rc = pthread_getunique_np(thread, &tid))) { - e = errno; - p = (struct PosixThread *)thread; - if (!__tkill(tid, sig, p->tib)) { - rc = 0; + int err = 0; + struct PosixThread *pt; + pt = (struct PosixThread *)thread; + if (!(1 <= sig && sig <= 64)) { + err = EINVAL; + } else if (thread == __get_tls()->tib_pthread) { + err = raise(sig); // XNU will EDEADLK it otherwise + } else if (atomic_load_explicit(&pt->status, memory_order_acquire) >= + kPosixThreadTerminated) { + err = ESRCH; + } else if (IsWindows()) { + err = __sig_kill(pt, sig, SI_TKILL); + } else { + if (IsXnuSilicon()) { + err = __syslib->__pthread_kill(_pthread_syshand(pt), sig); } else { - rc = errno; - errno = e; + int e = errno; + if (sys_tkill(_pthread_tid(pt), sig, pt->tib)) { + err = errno; + errno = e; + } + } + if (err == ESRCH) { + err = 0; // we already reported this } } - return rc; + STRACE("pthread_kill(%d, %G) → %s", _pthread_tid(pt), sig, + DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_reschedule.c b/libc/thread/pthread_reschedule.c index 18461a53c..bf0d1c8e1 100644 --- a/libc/thread/pthread_reschedule.c +++ b/libc/thread/pthread_reschedule.c @@ -25,24 +25,22 @@ #include "libc/thread/posixthread.internal.h" errno_t _pthread_reschedule(struct PosixThread *pt) { - int e, rc, tid; int policy = pt->attr.__schedpolicy; + int e, rc, tid = _pthread_tid(pt); struct sched_param param = {pt->attr.__schedparam}; - if (!(rc = pthread_getunique_np((pthread_t)pt, &tid))) { - e = errno; - if (IsNetbsd()) { - rc = sys_sched_setparam_netbsd(0, tid, policy, ¶m); - } else if (IsLinux()) { - rc = sys_sched_setscheduler(tid, policy, ¶m); - } else if (IsFreebsd()) { - rc = _pthread_setschedparam_freebsd(tid, policy, ¶m); - } else { - rc = enosys(); - } - if (rc == -1) { - rc = errno; - errno = e; - } + e = errno; + if (IsNetbsd()) { + rc = sys_sched_setparam_netbsd(0, tid, policy, ¶m); + } else if (IsLinux()) { + rc = sys_sched_setscheduler(tid, policy, ¶m); + } else if (IsFreebsd()) { + rc = _pthread_setschedparam_freebsd(tid, policy, ¶m); + } else { + rc = enosys(); + } + if (rc == -1) { + rc = errno; + errno = e; } return rc; } diff --git a/libc/thread/pthread_self.c b/libc/thread/pthread_self.c index 0be3110c2..ee6e96ea2 100644 --- a/libc/thread/pthread_self.c +++ b/libc/thread/pthread_self.c @@ -25,8 +25,5 @@ * @asyncsignalsafe */ pthread_t pthread_self(void) { - pthread_t t; - t = __get_tls()->tib_pthread; - unassert(t); - return t; + return __get_tls()->tib_pthread; } diff --git a/libc/thread/pthread_setaffinity_np.c b/libc/thread/pthread_setaffinity_np.c index 2b7b06886..25f78fd2e 100644 --- a/libc/thread/pthread_setaffinity_np.c +++ b/libc/thread/pthread_setaffinity_np.c @@ -31,15 +31,12 @@ #include "libc/thread/posixthread.internal.h" static dontinline textwindows int sys_pthread_setaffinity_nt( - int tid, uint64_t size, const cpu_set_t *bitset) { - int rc; - int64_t h; - h = OpenThread(kNtThreadSetInformation | kNtThreadQueryInformation, false, - tid); - if (!h) return __winerr(); - rc = SetThreadAffinityMask(h, bitset->__bits[0]) ? 0 : __winerr(); - CloseHandle(h); - return rc; + struct PosixThread *pt, uint64_t size, const cpu_set_t *bitset) { + if (SetThreadAffinityMask(_pthread_syshand(pt), bitset->__bits[0])) { + return 0; + } else { + return __winerr(); + } } /** @@ -54,24 +51,25 @@ static dontinline textwindows int sys_pthread_setaffinity_nt( errno_t pthread_setaffinity_np(pthread_t thread, size_t size, const cpu_set_t *bitset) { int e, rc, tid; - if (!(rc = pthread_getunique_np(thread, &tid))) { - e = errno; - if (size != sizeof(cpu_set_t)) { - rc = einval(); - } else if (IsWindows()) { - rc = sys_pthread_setaffinity_nt(tid, size, bitset); - } else if (IsFreebsd()) { - rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, - 32, bitset); - } else if (IsNetbsd()) { - rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset); - } else { - rc = sys_sched_setaffinity(tid, size, bitset); - } - if (rc == -1) { - rc = errno; - errno = e; - } + struct PosixThread *pt; + e = errno; + pt = (struct PosixThread *)thread; + tid = _pthread_tid(pt); + if (size != sizeof(cpu_set_t)) { + rc = einval(); + } else if (IsWindows()) { + rc = sys_pthread_setaffinity_nt(pt, size, bitset); + } else if (IsFreebsd()) { + rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, + bitset); + } else if (IsNetbsd()) { + rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset); + } else { + rc = sys_sched_setaffinity(tid, size, bitset); + } + if (rc == -1) { + rc = errno; + errno = e; } STRACE("pthread_setaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset, DescribeErrno(rc)); diff --git a/libc/thread/pthread_setcanceltype.c b/libc/thread/pthread_setcanceltype.c index b421dc81c..6a779a9c6 100644 --- a/libc/thread/pthread_setcanceltype.c +++ b/libc/thread/pthread_setcanceltype.c @@ -18,10 +18,23 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" #include "libc/errno.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" +#include "libc/mem/alloca.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" +static const char *DescribeCancelType(char buf[12], int err, int *t) { + if (err) return "n/a"; + if (!t) return "NULL"; + if (*t == PTHREAD_CANCEL_DEFERRED) return "PTHREAD_CANCEL_DEFERRED"; + if (*t == PTHREAD_CANCEL_ASYNCHRONOUS) return "PTHREAD_CANCEL_ASYNCHRONOUS"; + FormatInt32(buf, *t); + return buf; +} + /** * Sets cancellation strategy. * @@ -35,29 +48,32 @@ * @see pthread_cancel() for docs */ errno_t pthread_setcanceltype(int type, int *oldtype) { + int err; struct PosixThread *pt; switch (type) { case PTHREAD_CANCEL_ASYNCHRONOUS: - if (IsWindows()) { - return ENOTSUP; - } - // fallthrough case PTHREAD_CANCEL_DEFERRED: - pt = (struct PosixThread *)__get_tls()->tib_pthread; + pt = _pthread_self(); if (oldtype) { - if (pt->flags & PT_ASYNC) { + if (pt->pt_flags & PT_ASYNC) { *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS; } else { *oldtype = PTHREAD_CANCEL_DEFERRED; } } if (type == PTHREAD_CANCEL_DEFERRED) { - pt->flags &= ~PT_ASYNC; + pt->pt_flags &= ~PT_ASYNC; } else { - pt->flags |= PT_ASYNC; + pt->pt_flags |= PT_ASYNC; } - return 0; + err = 0; + break; default: - return EINVAL; + err = EINVAL; + break; } + STRACE("pthread_setcanceltype(%s, [%s]) → %s", + DescribeCancelType(alloca(12), 0, &type), + DescribeCancelType(alloca(12), err, oldtype), DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_setname_np.c b/libc/thread/pthread_setname_np.c index b7b30bb07..c6d01f9fc 100644 --- a/libc/thread/pthread_setname_np.c +++ b/libc/thread/pthread_setname_np.c @@ -24,20 +24,32 @@ #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" +#include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pr.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" +#include "libc/thread/tls.h" -static errno_t pthread_setname_impl(pthread_t thread, const char *name) { +static errno_t pthread_setname_impl(struct PosixThread *pt, const char *name) { char path[128], *p; int e, fd, rc, tid, len; - if ((rc = pthread_getunique_np(thread, &tid))) return rc; len = strlen(name); + tid = _pthread_tid(pt); - if (IsLinux()) { + if (IsXnuSilicon()) { + if (pt == _pthread_self()) { + __syslib->__pthread_setname_np(name); + return 0; + } else { + return EPERM; + } + } else if (IsLinux()) { e = errno; if (tid == gettid()) { if (prctl(PR_SET_NAME, name) == -1) { @@ -111,13 +123,17 @@ static errno_t pthread_setname_impl(pthread_t thread, const char *name) { * @return 0 on success, or errno on error * @raise ERANGE if length of `name` exceeded system limit, in which * case the name may have still been set with os using truncation - * @raise ENOSYS on MacOS, and Windows + * @raise ENOSYS on Windows and AMD64-XNU * @see pthread_getname_np() */ errno_t pthread_setname_np(pthread_t thread, const char *name) { - errno_t rc; + errno_t err; + struct PosixThread *pt; + pt = (struct PosixThread *)thread; BLOCK_CANCELLATIONS; - rc = pthread_setname_impl(thread, name); + err = pthread_setname_impl(pt, name); ALLOW_CANCELLATIONS; - return rc; + STRACE("pthread_setname_np(%d, %s) → %s", _pthread_tid(pt), name, + DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_timedjoin_np.c b/libc/thread/pthread_timedjoin_np.c index a31ef32c8..d636ac926 100644 --- a/libc/thread/pthread_timedjoin_np.c +++ b/libc/thread/pthread_timedjoin_np.c @@ -17,14 +17,67 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/cp.internal.h" #include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timespec.internal.h" #include "libc/errno.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/dll.h" +#include "libc/intrin/strace.internal.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread2.h" #include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" +#include "third_party/nsync/futex.internal.h" + +// TODO(jart): Use condition variable for thread waiting. + +static const char *DescribeReturnValue(char buf[30], int err, void **value) { + char *p = buf; + if (!value) return "NULL"; + if (err) return "[n/a]"; + *p++ = '['; + p = FormatHex64(p, (uintptr_t)*value, 1); + *p++ = ']'; + return buf; +} + +/** + * Blocks until memory location becomes zero. + * + * This is intended to be used on the child thread id, which is updated + * by the clone() system call when a thread terminates. We need this in + * order to know when it's safe to free a thread's stack. This function + * uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other + * platforms this uses polling with exponential backoff. + * + * @return 0 on success, or errno on error + * @raise ECANCELED if calling thread was cancelled in masked mode + * @raise EBUSY if `abstime` was specified and deadline expired + * @cancellationpoint + */ +static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) { + int x, e, rc = 0; + unassert(ctid != &__get_tls()->tib_tid); + // "If the thread calling pthread_join() is canceled, then the target + // thread shall not be detached." ──Quoth POSIX.1-2017 + if (!(rc = pthread_testcancel_np())) { + BEGIN_CANCELLATION_POINT; + while ((x = atomic_load_explicit(ctid, memory_order_acquire))) { + e = nsync_futex_wait_(ctid, x, !IsWindows(), abstime); + if (e == -ECANCELED) { + rc = ECANCELED; + break; + } else if (e == -ETIMEDOUT) { + rc = EBUSY; + break; + } + } + END_CANCELLATION_POINT; + } + return rc; +} /** * Waits for thread to terminate. @@ -50,7 +103,7 @@ */ errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr, struct timespec *abstime) { - errno_t rc; + errno_t err; struct PosixThread *pt; enum PosixThreadStatus status; pt = (struct PosixThread *)thread; @@ -59,15 +112,18 @@ errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr, // argument to pthread_join() does not refer to a joinable thread." // ──Quoth POSIX.1-2017 unassert(status == kPosixThreadJoinable || status == kPosixThreadTerminated); - if (!(rc = _wait0(&pt->tib->tib_tid, abstime))) { + if (!(err = _pthread_wait(&pt->tib->tib_tid, abstime))) { pthread_spin_lock(&_pthread_lock); dll_remove(&_pthread_list, &pt->list); pthread_spin_unlock(&_pthread_lock); if (value_ptr) { *value_ptr = pt->rc; } - _pthread_free(pt); - pthread_decimate_np(); + _pthread_free(pt, false); + _pthread_decimate(); } - return 0; + STRACE("pthread_timedjoin_np(%d, %s, %s) → %s", _pthread_tid(pt), + DescribeReturnValue(alloca(30), err, value_ptr), + DescribeTimespec(err ? -1 : 0, abstime), DescribeErrno(err)); + return err; } diff --git a/libc/thread/pthread_zombify.c b/libc/thread/pthread_zombify.c index 521e8ae91..eaf2f9dde 100644 --- a/libc/thread/pthread_zombify.c +++ b/libc/thread/pthread_zombify.c @@ -23,6 +23,6 @@ void _pthread_zombify(struct PosixThread *pt) { pthread_spin_lock(&_pthread_lock); dll_remove(&_pthread_list, &pt->list); - dll_make_first(&_pthread_list, &pt->list); + dll_make_last(&_pthread_list, &pt->list); pthread_spin_unlock(&_pthread_lock); } diff --git a/libc/thread/sem_open.c b/libc/thread/sem_open.c index 542e5ac00..a73269b80 100644 --- a/libc/thread/sem_open.c +++ b/libc/thread/sem_open.c @@ -57,13 +57,13 @@ static void sem_open_unlock(void) { pthread_mutex_unlock(&g_semaphores.lock); } -static void sem_open_funlock(void) { +static void sem_open_wipe(void) { pthread_mutex_init(&g_semaphores.lock, 0); } static void sem_open_setup(void) { - sem_open_funlock(); - pthread_atfork(sem_open_lock, sem_open_unlock, sem_open_funlock); + sem_open_wipe(); + pthread_atfork(sem_open_lock, sem_open_unlock, sem_open_wipe); } static void sem_open_init(void) { diff --git a/libc/calls/setitimer.c b/libc/thread/setitimer.c similarity index 100% rename from libc/calls/setitimer.c rename to libc/thread/setitimer.c diff --git a/libc/thread/spawn.c b/libc/thread/spawn.c deleted file mode 100644 index b83554841..000000000 --- a/libc/thread/spawn.c +++ /dev/null @@ -1,166 +0,0 @@ -/*-*- 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/thread/spawn.h" -#include "libc/assert.h" -#include "libc/calls/calls.h" -#include "libc/errno.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" -#include "libc/stdalign.internal.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/clone.h" -#include "libc/sysv/consts/map.h" -#include "libc/sysv/consts/prot.h" -#include "libc/sysv/errfuns.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" - -/** - * @fileoverview Simple threading API - * - * This API is supported on all six operating systems. We have this - * because the POSIX threads API is positively enormous. We currently - * only implement a small subset of POSIX threads, e.g. mutexes. So - * until we can implement all of POSIX threads, this API is great. If we - * consider that the classic forking concurrency library consists of a - * single function, it's a shame POSIX didn't define threads in the past - * to just be this. Since create/join/atomics is really all we need. - * - * Your spawn library abstracts clone() which also works on all - * platforms; however our implementation of clone() is significantly - * complicated so we strongly recommend always favoring this API. - * - * @deprecated - */ - -#define _TLSZ ((intptr_t)_tls_size) -#define _TLDZ ((intptr_t)_tdata_size) -#define _TIBZ sizeof(struct CosmoTib) -#define _MEMZ ROUNDUP(_TLSZ + _TIBZ, alignof(struct CosmoTib)) - -struct spawner { - int (*fun)(void *, int); - void *arg; -}; - -static int Spawner(void *arg, int tid) { - int rc; - struct spawner *spawner = arg; - rc = spawner->fun(spawner->arg, tid); - _pthread_ungarbage(); - free(spawner); - return rc; -} - -/** - * Spawns thread, e.g. - * - * int worker(void *arg, int tid) { - * const char *s = arg; - * printf("%s\n", s); - * return 0; - * } - * - * int main() { - * struct spawn th; - * _spawn(worker, "hi", &th); - * _join(&th); - * } - * - * @param fun is thread worker callback, which receives `arg` and `ctid` - * @param arg shall be passed to `fun` - * @param opt_out_thread needn't be initialiized and is always clobbered - * except when it isn't specified, in which case, the thread is kind - * of detached and will (currently) just leak the stack / tls memory - * @return 0 on success, or -1 w/ errno - * @deprecated - */ -int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { - errno_t rc; - struct spawn *th, ths; - struct spawner *spawner; - __require_tls(); - if (!fun) return einval(); - - // we need to to clobber the output memory before calling clone, since - // there's no guarantee clone() won't suspend the parent, and focus on - // running the child instead; in that case child might want to read it - if (opt_out_thread) { - th = opt_out_thread; - } else { - th = &ths; - } - - // allocate enough TLS memory for all the GNU Linuker (_tls_size) - // organized _Thread_local data, as well as Cosmpolitan Libc (64) - if (!(th->tls = _mktls(&th->tib))) { - return -1; - } - - // we must use _mapstack() to allocate the stack because OpenBSD has - // very strict requirements for what's allowed to be used for stacks - if (!(th->stk = NewCosmoStack())) { - free(th->tls); - return -1; - } - - spawner = malloc(sizeof(struct spawner)); - spawner->fun = fun; - spawner->arg = arg; - rc = clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */, - CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | - CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, - spawner, &th->ptid, __adj_tls(th->tib), &th->tib->tib_tid); - if (rc) { - errno = rc; - FreeCosmoStack(th->stk); - free(th->tls); - return -1; - } - - return 0; -} - -/** - * Waits for thread created by _spawn() to terminate. - * - * This will free your thread's stack and tls memory too. - * - * @deprecated - */ -int _join(struct spawn *th) { - int rc; - if (th->tib) { - // wait for ctid to become zero - npassert(!_wait0(&th->tib->tib_tid, 0)); - // free thread memory - free(th->tls); - rc = munmap(th->stk, GetStackSize()); - rc = 0; - } else { - rc = 0; - } - bzero(th, sizeof(*th)); - return rc; -} diff --git a/libc/thread/spawn.h b/libc/thread/spawn.h deleted file mode 100644 index f6c1b16d6..000000000 --- a/libc/thread/spawn.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_THREAD_SPAWN_H_ -#define COSMOPOLITAN_LIBC_THREAD_SPAWN_H_ -#include "libc/thread/tls.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct spawn { - int ptid; - char *stk; - char *tls; - struct CosmoTib *tib; -}; - -int _spawn(int (*)(void *, int), void *, struct spawn *); -int _join(struct spawn *); -char *_mktls(struct CosmoTib **); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_SPAWN_H_ */ diff --git a/libc/thread/thread.h b/libc/thread/thread.h index 708e15306..a49a9c033 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -2,7 +2,7 @@ #define COSMOPOLITAN_LIBC_THREAD_THREAD_H_ #define PTHREAD_KEYS_MAX 128 -#define PTHREAD_STACK_MIN 262144 +#define PTHREAD_STACK_MIN 65536 #define PTHREAD_DESTRUCTOR_ITERATIONS 4 #define PTHREAD_BARRIER_SERIAL_THREAD 31337 @@ -97,10 +97,10 @@ typedef struct pthread_attr_s { int __schedparam; int __schedpolicy; int __contentionscope; - unsigned __guardsize; - unsigned __stacksize; + int __guardsize; + size_t __stacksize; uint32_t __sigmask[4]; - char *__stackaddr; + void *__stackaddr; } pthread_attr_t; struct _pthread_cleanup_buffer { @@ -199,7 +199,6 @@ pthread_t pthread_self(void) pureconst; void *pthread_getspecific(pthread_key_t); void pthread_cleanup_pop(struct _pthread_cleanup_buffer *, int) paramsnonnull(); void pthread_cleanup_push(struct _pthread_cleanup_buffer *, void (*)(void *), void *) paramsnonnull((1)); -void pthread_decimate_np(void); void pthread_exit(void *) wontreturn; void pthread_testcancel(void); diff --git a/libc/thread/thread.mk b/libc/thread/thread.mk index 0f7d2eb57..2faa73c9f 100644 --- a/libc/thread/thread.mk +++ b/libc/thread/thread.mk @@ -49,6 +49,12 @@ $(LIBC_THREAD_A).pkg: \ $(LIBC_THREAD_A_OBJS) \ $(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg) +$(LIBC_THREAD_A_OBJS): private \ + COPTS += \ + -fno-sanitize=all \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x))) LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS)) LIBC_THREAD_HDRS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/thread/tls.h b/libc/thread/tls.h index 4541e8ad2..f0677b4fc 100644 --- a/libc/thread/tls.h +++ b/libc/thread/tls.h @@ -17,6 +17,9 @@ struct CosmoFtrace { /* 16 */ int64_t ft_lastaddr; /* 8 */ }; +/* NOTE: update aarch64 libc/errno.h if sizeof changes */ +/* NOTE: update aarch64 libc/proc/vfork.S if sizeof changes */ +/* NOTE: update aarch64 libc/nexgen32e/gc.S if sizeof changes */ struct CosmoTib { struct CosmoTib *tib_self; /* 0x00 */ struct CosmoFtrace tib_ftracer; /* 0x08 */ @@ -29,54 +32,54 @@ struct CosmoTib { uint64_t tib_flags; /* 0x40 */ int tib_ftrace; /* inherited */ int tib_strace; /* inherited */ - uint64_t tib_sigmask; /* inherited */ - uint64_t tib_sigpending; - void *tib_reserved4; - void *tib_reserved5; - void *tib_reserved6; - void *tib_reserved7; - void *tib_keys[128]; + _Atomic(uint64_t) tib_sigmask; /* inherited */ + _Atomic(uint64_t) tib_sigpending; + _Atomic(uint64_t) tib_syshand; /* win32=kThread, xnusilicon=pthread_t */ + char *tib_sigstack_addr; + uint32_t tib_sigstack_size; + uint32_t tib_sigstack_flags; + void **tib_keys; }; extern int __threaded; extern unsigned __tls_index; +char *_mktls(struct CosmoTib **); +void __bootstrap_tls(struct CosmoTib *, char *); + #ifdef __x86_64__ extern bool __tls_enabled; #define __tls_enabled_set(x) __tls_enabled = x #elif defined(__aarch64__) -#define __tls_enabled \ - ({ \ - register struct CosmoTib *_t asm("x28"); \ - !!_t; \ - }) +#define __tls_enabled true #define __tls_enabled_set(x) (void)0 #else #error "unsupported architecture" #endif -void __require_tls(void); void __set_tls(struct CosmoTib *); -#ifdef __x86_64__ /** * Returns location of thread information block. * * This can't be used in privileged functions. */ -#define __get_tls() \ - ({ \ - struct CosmoTib *_t; \ - asm("mov\t%%fs:0,%0" : "=r"(_t) : /* no inputs */ : "memory"); \ - _t; \ - }) +__funline pureconst struct CosmoTib *__get_tls(void) { +#ifdef __chibicc__ + return 0; +#elif __x86_64__ + struct CosmoTib *__tib; + __asm__("mov\t%%fs:0,%0" : "=r"(__tib)); + return __tib; +#elif defined(__aarch64__) + register struct CosmoTib *__tls __asm__("x28"); + return __tls - 1; +#endif +} + +#ifdef __x86_64__ #define __adj_tls(tib) (tib) #elif defined(__aarch64__) -#define __get_tls() \ - ({ \ - register struct CosmoTib *_t asm("x28"); \ - _t - 1; \ - }) #define __adj_tls(tib) ((struct CosmoTib *)(tib) + 1) #endif diff --git a/libc/thread/tls2.internal.h b/libc/thread/tls2.internal.h index d046f8d35..707dddd8a 100644 --- a/libc/thread/tls2.internal.h +++ b/libc/thread/tls2.internal.h @@ -25,14 +25,14 @@ __funline struct CosmoTib *__get_tls_privileged(void) { return (struct CosmoTib *)tib; } -static dontasan inline struct CosmoTib *__get_tls_win32(void) { +__funline struct CosmoTib *__get_tls_win32(void) { char *tib, *lin = (char *)0x30; asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory"); tib = *(char **)(tib + 0x1480 + __tls_index * 8); return (struct CosmoTib *)tib; } -static dontasan inline void __set_tls_win32(void *tls) { +__funline void __set_tls_win32(void *tls) { asm("mov\t%1,%%gs:%0" : "=m"(*((long *)0x1480 + __tls_index)) : "r"(tls)); } diff --git a/libc/calls/ualarm.c b/libc/thread/ualarm.c similarity index 100% rename from libc/calls/ualarm.c rename to libc/thread/ualarm.c diff --git a/libc/thread/wait0.c b/libc/thread/wait0.c deleted file mode 100644 index 0ad3ca213..000000000 --- a/libc/thread/wait0.c +++ /dev/null @@ -1,67 +0,0 @@ -/*-*- 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/assert.h" -#include "libc/calls/cp.internal.h" -#include "libc/calls/struct/timespec.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/atomic.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" -#include "third_party/nsync/futex.internal.h" - -/** - * Blocks until memory location becomes zero. - * - * This is intended to be used on the child thread id, which is updated - * by the clone() system call when a thread terminates. We need this in - * order to know when it's safe to free a thread's stack. This function - * uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other - * platforms this uses polling with exponential backoff. - * - * @return 0 on success, or errno on error - * @raise ECANCELED if calling thread was cancelled in masked mode - * @raise EBUSY if `abstime` was specified and deadline expired - * @cancellationpoint - */ -errno_t _wait0(atomic_int *ctid, struct timespec *abstime) { - int x, e, rc = 0; - // "The behavior is undefined if the value specified by the thread - // argument to pthread_join() refers to the calling thread." - // ──Quoth POSIX.1-2017 - unassert(ctid != &__get_tls()->tib_tid); - // "If the thread calling pthread_join() is canceled, then the target - // thread shall not be detached." ──Quoth POSIX.1-2017 - if (!(rc = pthread_testcancel_np())) { - BEGIN_CANCELLATION_POINT; - while ((x = atomic_load_explicit(ctid, memory_order_acquire))) { - e = nsync_futex_wait_(ctid, x, !IsWindows(), abstime); - if (e == -ECANCELED) { - rc = ECANCELED; - break; - } else if (e == -ETIMEDOUT) { - rc = EBUSY; - break; - } - } - END_CANCELLATION_POINT; - } - return rc; -} diff --git a/libc/thread/wait0.internal.h b/libc/thread/wait0.internal.h deleted file mode 100644 index d2fc59aab..000000000 --- a/libc/thread/wait0.internal.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_WAIT0_H_ -#define COSMOPOLITAN_LIBC_INTRIN_WAIT0_H_ -#include "libc/calls/struct/timespec.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -errno_t _wait0(_Atomic(int) *, struct timespec *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_WAIT0_H_ */ diff --git a/libc/time/localtime.c b/libc/time/localtime.c index ccc52662c..a69e31b5c 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -47,6 +47,10 @@ __static_yoink("usr/share/zoneinfo/UTC"); static pthread_mutex_t locallock; +void localtime_wipe(void) { + pthread_mutex_init(&locallock, 0); +} + void localtime_lock(void) { pthread_mutex_lock(&locallock); } @@ -55,15 +59,11 @@ void localtime_unlock(void) { pthread_mutex_unlock(&locallock); } -void localtime_funlock(void) { - pthread_mutex_init(&locallock, 0); -} - __attribute__((__constructor__)) static void localtime_init(void) { - localtime_funlock(); + localtime_wipe(); pthread_atfork(localtime_lock, localtime_unlock, - localtime_funlock); + localtime_wipe); } #ifndef TZ_ABBR_MAX_LEN diff --git a/libc/time/time.h b/libc/time/time.h index 6b3ee485f..3e9a0c1d2 100644 --- a/libc/time/time.h +++ b/libc/time/time.h @@ -23,10 +23,6 @@ extern const char kWeekdayName[7][10]; extern const char kMonthNameShort[12][4]; extern const char kMonthName[12][10]; extern const unsigned short kMonthYearDay[2][12]; -extern long double (*nowl)(void); -long double ConvertTicksToNanos(double); -long double dsleep(long double); -long double dtime(int); void RefreshTime(void); #endif /* _COSMO_SOURCE */ diff --git a/libc/time/time.mk b/libc/time/time.mk index 3a61a88bb..da781398b 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -43,6 +43,9 @@ LIBC_TIME_A_DIRECTDEPS = \ LIBC_TIME_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_TIME_A_DIRECTDEPS),$($(x)))) +# offer assurances about the stack safety of cosmo libc +$(LIBC_TIME_A_OBJS): private COPTS += -Wframe-larger-than=4096 -Walloca-larger-than=4096 + $(LIBC_TIME_A): libc/time/ \ $(LIBC_TIME_A).pkg \ $(LIBC_TIME_A_OBJS) diff --git a/libc/x/x.mk b/libc/x/x.mk index 22f61df36..9cc4b8d0c 100644 --- a/libc/x/x.mk +++ b/libc/x/x.mk @@ -30,7 +30,9 @@ LIBC_X_A_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ + LIBC_NT_KERNEL32 \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ diff --git a/libc/x/xspawn.c b/libc/x/xspawn.c index 60f2ddd39..bab32bea3 100644 --- a/libc/x/xspawn.c +++ b/libc/x/xspawn.c @@ -20,6 +20,8 @@ #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/nt/runtime.h" #include "libc/sysv/consts/sig.h" #include "libc/x/x.h" diff --git a/libc/x/xwrite.c b/libc/x/xwrite.c index 52bf01df2..c638c8c58 100644 --- a/libc/x/xwrite.c +++ b/libc/x/xwrite.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/blockcancel.internal.h" #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/x/x.h" @@ -26,18 +27,25 @@ * @return 0 on success, or -1 w/ errno */ int xwrite(int fd, const void *p, uint64_t n) { + int rc; int64_t i; uint64_t m; const char *buf; + BLOCK_CANCELLATIONS; + rc = 0; buf = p; while (n) { m = n; do { i = write(fd, buf, m); } while (i < 0 && errno == EINTR); - if (i < 0) return -1; + if (i < 0) { + rc = -1; + break; + } buf += i; n -= i; } - return 0; + ALLOW_CANCELLATIONS; + return rc; } diff --git a/net/https/choosecertificatelifetime.c b/net/https/choosecertificatelifetime.c index 310f34649..e4eb4c548 100644 --- a/net/https/choosecertificatelifetime.c +++ b/net/https/choosecertificatelifetime.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/timespec.h" #include "libc/time/struct/tm.h" -#include "libc/time/time.h" #include "net/https/https.h" void ChooseCertificateLifetime(char notbefore[16], char notafter[16]) { @@ -25,7 +25,7 @@ void ChooseCertificateLifetime(char notbefore[16], char notafter[16]) { int64_t past, now, future, lifetime, tolerance; tolerance = 60 * 60 * 24; lifetime = 60 * 60 * 24 * 365; - now = nowl(); + now = timespec_real().tv_sec; past = now - tolerance; future = now + tolerance + lifetime; FormatSslTime(notbefore, gmtime_r(&past, &tm)); diff --git a/net/turfwar/turfwar.c b/net/turfwar/turfwar.c index 15b551e53..65172f759 100644 --- a/net/turfwar/turfwar.c +++ b/net/turfwar/turfwar.c @@ -1958,7 +1958,6 @@ int main(int argc, char *argv[]) { } // library init - __enable_threads(); sqlite3_initialize(); CheckDatabase(); diff --git a/net/turfwar/turfwar.mk b/net/turfwar/turfwar.mk index b207c0263..2cb8516e3 100644 --- a/net/turfwar/turfwar.mk +++ b/net/turfwar/turfwar.mk @@ -23,6 +23,7 @@ NET_TURFWAR_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index 09ef0a405..0e9d1cf60 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -28,9 +28,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/chdir_test.c b/test/libc/calls/chdir_test.c index d02f73bec..69c1ad06d 100644 --- a/test/libc/calls/chdir_test.c +++ b/test/libc/calls/chdir_test.c @@ -22,9 +22,8 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c index 5fbf425ea..ec6079029 100644 --- a/test/libc/calls/clock_gettime_test.c +++ b/test/libc/calls/clock_gettime_test.c @@ -32,10 +32,6 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -TEST(clock_gettime, fault) { - ASSERT_SYS(EFAULT, -1, clock_gettime(0, 0)); -} - TEST(clock_gettime, test) { struct timespec ts = {0}; ASSERT_EQ(0, clock_gettime(0, &ts)); @@ -57,7 +53,6 @@ BENCH(clock_gettime, bench) { struct timespec ts; gettimeofday(&tv, 0); // trigger init clock_gettime(0, &ts); // trigger init - EZBENCH2("nowl", donothing, nowl()); EZBENCH2("rdtsc", donothing, rdtsc()); EZBENCH2("gettimeofday", donothing, gettimeofday(&tv, 0)); EZBENCH2("timespec_real", donothing, timespec_real()); diff --git a/test/libc/calls/clock_nanosleep_test.c b/test/libc/calls/clock_nanosleep_test.c index 5bd1ea2a6..775fe15ed 100644 --- a/test/libc/calls/clock_nanosleep_test.c +++ b/test/libc/calls/clock_nanosleep_test.c @@ -20,9 +20,11 @@ #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/timespec.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sa.h" @@ -30,15 +32,15 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" +void SetUpOnce(void) { + if (!IsWindows()) _Exit(0); +} + void OnAlrm(int sig) { // do nothing STRACE("OnAlrm()"); } -TEST(nanosleep, testFault) { - EXPECT_SYS(EFAULT, -1, nanosleep(0, 0)); -} - TEST(nanosleep, testInvalid) { struct timespec ts = {0, -1}; EXPECT_SYS(EINVAL, -1, nanosleep(&ts, 0)); diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index 2049d8083..88067ff6d 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -39,9 +39,9 @@ uint64_t i; char *oldpath; char tmp[PATH_MAX]; char pathbuf[PATH_MAX]; -char testlib_enable_tmp_setup_teardown; void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/copy_file_range_test.c b/test/libc/calls/copy_file_range_test.c index 040420ac9..15eadf3d0 100644 --- a/test/libc/calls/copy_file_range_test.c +++ b/test/libc/calls/copy_file_range_test.c @@ -32,7 +32,9 @@ #include "libc/x/x.h" #include "libc/x/xasprintf.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void Make(const char *path, int mode) { int fd, n = lemur64() & 0xfffff; diff --git a/test/libc/calls/dup_test.c b/test/libc/calls/dup_test.c index 4a78ed647..25556b522 100644 --- a/test/libc/calls/dup_test.c +++ b/test/libc/calls/dup_test.c @@ -21,7 +21,9 @@ #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" +#include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -31,7 +33,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/xspawn.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} static textstartup void TestInit(int argc, char **argv) { int fd; @@ -46,6 +50,7 @@ static textstartup void TestInit(int argc, char **argv) { const void *const TestCtor[] initarray = {TestInit}; +#if 0 TEST(dup, ebadf) { ASSERT_SYS(EBADF, -1, dup(-1)); ASSERT_SYS(EBADF, -1, dup2(-1, 0)); @@ -67,6 +72,7 @@ TEST(dup, bigNumber) { ASSERT_SYS(0, 100, dup2(0, 100)); ASSERT_SYS(0, 0, close(100)); } +#endif #ifdef __x86_64__ TEST(dup, clearsCloexecFlag) { diff --git a/test/libc/calls/fchdir_test.c b/test/libc/calls/fchdir_test.c index f80d8c594..ac67e6692 100644 --- a/test/libc/calls/fchdir_test.c +++ b/test/libc/calls/fchdir_test.c @@ -21,7 +21,9 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fchdir, test) { const char *a, *b; diff --git a/test/libc/calls/fcntl_test.c b/test/libc/calls/fcntl_test.c index f1ea64ca0..1dd17eab7 100644 --- a/test/libc/calls/fcntl_test.c +++ b/test/libc/calls/fcntl_test.c @@ -53,7 +53,9 @@ int Lock(int fd, int type, long start, long len) { return 0; } -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fcntl_getfl, testRemembersAccessMode) { int fd; diff --git a/test/libc/calls/fexecve_test.c b/test/libc/calls/fexecve_test.c index 357cd31c1..b97afbf25 100644 --- a/test/libc/calls/fexecve_test.c +++ b/test/libc/calls/fexecve_test.c @@ -33,7 +33,10 @@ __static_yoink("zipos"); int fds[2]; char buf[8]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp(void) { if (IsFreebsd()) exit(0); // TODO: fixme on freebsd diff --git a/test/libc/calls/fileexists_test.c b/test/libc/calls/fileexists_test.c index 7f73c472f..7cffb37fd 100644 --- a/test/libc/calls/fileexists_test.c +++ b/test/libc/calls/fileexists_test.c @@ -21,9 +21,8 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/ftruncate_test.c b/test/libc/calls/ftruncate_test.c index a859065c6..d3bb70a84 100644 --- a/test/libc/calls/ftruncate_test.c +++ b/test/libc/calls/ftruncate_test.c @@ -35,9 +35,8 @@ struct stat st; -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath", 0)); } diff --git a/test/libc/calls/getcontext_test.c b/test/libc/calls/getcontext_test.c index 82dccdefd..66b0b7563 100644 --- a/test/libc/calls/getcontext_test.c +++ b/test/libc/calls/getcontext_test.c @@ -54,7 +54,7 @@ TEST(getcontext, test) { TEST(getcontext, canReadAndWriteSignalMask) { sigset_t ss, old; volatile int n = 0; - EXPECT_TRUE(__interruptible); + __interruptible = true; sigemptyset(&ss); sigaddset(&ss, SIGUSR1); sigprocmask(SIG_SETMASK, &ss, &old); diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index 5582d1a58..76dad7bf1 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -26,9 +26,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath cpath fattr", 0)); } diff --git a/test/libc/calls/lock2_test.c b/test/libc/calls/lock2_test.c index ae883eb65..e38fabbbe 100644 --- a/test/libc/calls/lock2_test.c +++ b/test/libc/calls/lock2_test.c @@ -28,7 +28,9 @@ #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(lock, wholeFile) { ASSERT_SYS(0, 3, open("db", O_RDWR | O_CREAT | O_EXCL, 0644)); diff --git a/test/libc/calls/lock_ofd_test.c b/test/libc/calls/lock_ofd_test.c index 67ef5eac6..55517c86d 100644 --- a/test/libc/calls/lock_ofd_test.c +++ b/test/libc/calls/lock_ofd_test.c @@ -39,7 +39,9 @@ #define RATIO 3 #define ITERATIONS 10 -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} _Thread_local const char *kind; diff --git a/test/libc/calls/lock_test.c b/test/libc/calls/lock_test.c index 9afe24bec..bc08ceccb 100644 --- a/test/libc/calls/lock_test.c +++ b/test/libc/calls/lock_test.c @@ -36,7 +36,9 @@ #define RATIO 3 #define ITERATIONS 10 -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} _Thread_local const char *kind; diff --git a/test/libc/calls/lseek_test.c b/test/libc/calls/lseek_test.c index 9d6af1477..04146c83b 100644 --- a/test/libc/calls/lseek_test.c +++ b/test/libc/calls/lseek_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" @@ -32,91 +33,95 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr proc inet", 0)); } -TEST(lseek, ebadf) { - ASSERT_SYS(EBADF, -1, lseek(-1, 0, SEEK_SET)); - ASSERT_SYS(EBADF, -1, lseek(+3, 0, SEEK_SET)); -} +/* TEST(lseek, ebadf) { */ +/* ASSERT_SYS(EBADF, -1, lseek(-1, 0, SEEK_SET)); */ +/* ASSERT_SYS(EBADF, -1, lseek(+3, 0, SEEK_SET)); */ +/* } */ -TEST(lseek, badWhence_einval) { - ASSERT_SYS(0, 3, creat("foo", 0644)); - ASSERT_SYS(EINVAL, -1, lseek(3, 0, -1)); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, badWhence_einval) { */ +/* ASSERT_SYS(0, 3, creat("foo", 0644)); */ +/* ASSERT_SYS(EINVAL, -1, lseek(3, 0, -1)); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ TEST(lseek, negativeComputedOffset_einval) { ASSERT_SYS(0, 3, creat("foo", 0644)); ASSERT_SYS(EINVAL, -1, lseek(3, -1, SEEK_SET)); ASSERT_SYS(EINVAL, -1, lseek(3, -1, SEEK_CUR)); + ASSERT_SYS(0, 0, lseek(3, 0, SEEK_END)); ASSERT_SYS(EINVAL, -1, lseek(3, -1, SEEK_END)); + ASSERT_SYS(0, 10, lseek(3, 10, SEEK_END)); + ASSERT_SYS(0, 5, lseek(3, 5, SEEK_END)); EXPECT_SYS(0, 0, close(3)); } -TEST(lseek, 64bit) { - ASSERT_SYS(0, 3, creat("foo", 0644)); - ASSERT_SYS(0, 0x100000001, lseek(3, 0x100000001, SEEK_SET)); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, 64bit) { */ +/* ASSERT_SYS(0, 3, creat("foo", 0644)); */ +/* ASSERT_SYS(0, 0x100000001, lseek(3, 0x100000001, SEEK_SET)); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ -TEST(lseek, isPipe_ESPIPE) { - int fds[2]; - char buf[2]; - ASSERT_SYS(0, 0, pipe(fds)); - ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); - ASSERT_SYS(ESPIPE, -1, pwrite(4, "hi", 2, 0)); - ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); - EXPECT_SYS(0, 0, close(4)); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, isPipe_ESPIPE) { */ +/* int fds[2]; */ +/* char buf[2]; */ +/* ASSERT_SYS(0, 0, pipe(fds)); */ +/* ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); */ +/* ASSERT_SYS(ESPIPE, -1, pwrite(4, "hi", 2, 0)); */ +/* ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); */ +/* EXPECT_SYS(0, 0, close(4)); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ -TEST(lseek, isSocket_ESPIPE) { - char buf[2]; - ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); - ASSERT_SYS(ESPIPE, -1, pwrite(3, "hi", 2, 0)); - ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, isSocket_ESPIPE) { */ +/* char buf[2]; */ +/* ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); */ +/* ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); */ +/* ASSERT_SYS(ESPIPE, -1, pwrite(3, "hi", 2, 0)); */ +/* ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ -TEST(lseek, filePositionChanges_areObservableAcrossDup) { - ASSERT_SYS(0, 3, creat("wut", 0644)); - ASSERT_SYS(0, 4, dup(3)); - ASSERT_SYS(0, 0, lseek(3, 0, SEEK_CUR)); - ASSERT_SYS(0, 1, lseek(4, 1, SEEK_SET)); - ASSERT_SYS(0, 1, lseek(3, 0, SEEK_CUR)); - EXPECT_SYS(0, 0, close(4)); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, filePositionChanges_areObservableAcrossDup) { */ +/* if (IsWindows()) return; // do not want to support */ +/* ASSERT_SYS(0, 3, creat("wut", 0644)); */ +/* ASSERT_SYS(0, 4, dup(3)); */ +/* ASSERT_SYS(0, 0, lseek(3, 0, SEEK_CUR)); */ +/* ASSERT_SYS(0, 1, lseek(4, 1, SEEK_SET)); */ +/* ASSERT_SYS(0, 1, lseek(3, 0, SEEK_CUR)); */ +/* EXPECT_SYS(0, 0, close(4)); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ -TEST(lseek, filePositionChanges_areObservableAcrossProcesses) { - char buf[8] = {0}; - ASSERT_SYS(0, 3, open("wut", O_RDWR | O_CREAT, 0644)); - ASSERT_SYS(0, 3, write(3, "wut", 3)); - ASSERT_SYS(0, 0, lseek(3, 0, SEEK_SET)); - SPAWN(fork); - ASSERT_SYS(0, 1, lseek(3, 1, SEEK_SET)); - EXITS(0); - EXPECT_SYS(0, 1, read(3, buf, 1)); - EXPECT_EQ('u', buf[0]); - EXPECT_SYS(0, 0, close(3)); -} +/* TEST(lseek, filePositionChanges_areObservableAcrossProcesses) { */ +/* if (IsWindows()) return; // do not want to support */ +/* char buf[8] = {0}; */ +/* ASSERT_SYS(0, 3, open("wut", O_RDWR | O_CREAT, 0644)); */ +/* ASSERT_SYS(0, 3, write(3, "wut", 3)); */ +/* ASSERT_SYS(0, 0, lseek(3, 0, SEEK_SET)); */ +/* SPAWN(fork); */ +/* ASSERT_SYS(0, 1, lseek(3, 1, SEEK_SET)); */ +/* EXITS(0); */ +/* EXPECT_SYS(0, 1, read(3, buf, 1)); */ +/* EXPECT_EQ('u', buf[0]); */ +/* EXPECT_SYS(0, 0, close(3)); */ +/* } */ -TEST(lseek, beyondEndOfFile_isZeroExtendedUponSubsequentWrite) { - char buf[8] = {1, 1}; - ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); - ASSERT_SYS(0, 2, lseek(3, 2, SEEK_SET)); - ASSERT_SYS(0, 2, lseek(3, 0, SEEK_CUR)); - ASSERT_SYS(0, 0, pread(3, buf, 8, 0)); // lseek() alone doesn't extend - ASSERT_SYS(0, 2, write(3, buf, 2)); // does extend once i/o happens - ASSERT_SYS(0, 4, pread(3, buf, 8, 0)); - ASSERT_EQ(0, buf[0]); - ASSERT_EQ(0, buf[1]); - ASSERT_EQ(1, buf[2]); - ASSERT_EQ(1, buf[3]); - ASSERT_SYS(0, 0, close(3)); -} +/* TEST(lseek, beyondEndOfFile_isZeroExtendedUponSubsequentWrite) { */ +/* char buf[8] = {1, 1}; */ +/* ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); */ +/* ASSERT_SYS(0, 2, lseek(3, 2, SEEK_SET)); */ +/* ASSERT_SYS(0, 2, lseek(3, 0, SEEK_CUR)); */ +/* ASSERT_SYS(0, 0, pread(3, buf, 8, 0)); // lseek() alone doesn't extend */ +/* ASSERT_SYS(0, 2, write(3, buf, 2)); // does extend once i/o happens */ +/* ASSERT_SYS(0, 4, pread(3, buf, 8, 0)); */ +/* ASSERT_EQ(0, buf[0]); */ +/* ASSERT_EQ(0, buf[1]); */ +/* ASSERT_EQ(1, buf[2]); */ +/* ASSERT_EQ(1, buf[3]); */ +/* ASSERT_SYS(0, 0, close(3)); */ +/* } */ diff --git a/test/libc/calls/makedirs_test.c b/test/libc/calls/makedirs_test.c index c85077001..cf3eb0eca 100644 --- a/test/libc/calls/makedirs_test.c +++ b/test/libc/calls/makedirs_test.c @@ -22,7 +22,6 @@ #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #include "libc/x/x.h" @@ -58,9 +57,12 @@ TEST(makedirs, basic) { "L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z" pthread_barrier_t barrier; -char testlib_enable_tmp_setup_teardown; -int Worker(void *arg, int tid) { +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + +void *Worker(void *arg) { pthread_barrier_wait(&barrier); ASSERT_EQ(0, makedirs(DIR, 0755)); return 0; @@ -69,9 +71,9 @@ int Worker(void *arg, int tid) { TEST(makedirs, test) { if (IsWindows()) return; // todo: why won't long paths work on windows int i, n = 8; - struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0)); + for (i = 0; i < n; ++i) EXPECT_EQ(0, pthread_join(t[i], 0)); ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); } diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index c3427349a..fb8029ddb 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -31,9 +31,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 456b48548..48b8b6a7b 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/errno.h" #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" diff --git a/test/libc/calls/mkntenvblock_test.c b/test/libc/calls/mkntenvblock_test.c index 7ddd6eccd..97ab584a9 100644 --- a/test/libc/calls/mkntenvblock_test.c +++ b/test/libc/calls/mkntenvblock_test.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntspawn.h" +#include "libc/proc/ntspawn.h" #include "libc/mem/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index 853b666c0..0ef42909f 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -40,9 +40,8 @@ #define abs(rel) gc(xasprintf("%s/%s", gc(getcwd(0, 0)), rel)) -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr proc id", 0)); } @@ -449,13 +448,13 @@ TEST(open, creatFile_touchesDirectory) { ASSERT_SYS(0, 0, stat("dir", &st)); birth = st.st_ctim; // check we can read time without changing it - sleep(1); + sleep(2); ASSERT_SYS(0, 0, stat("dir", &st)); EXPECT_EQ(0, timespec_cmp(st.st_ctim, birth)); EXPECT_EQ(0, timespec_cmp(st.st_mtim, birth)); EXPECT_EQ(0, timespec_cmp(st.st_atim, birth)); // check that the directory time changes when file is made - sleep(1); + sleep(2); ASSERT_SYS(0, 0, touch("dir/file", 0644)); ASSERT_SYS(0, 0, stat("dir", &st)); EXPECT_EQ(1, timespec_cmp(st.st_ctim, birth)); @@ -473,7 +472,7 @@ TEST(open, trunc_touchesMtimCtim) { ASSERT_SYS(0, 0, touch("regular", 0755)); ASSERT_SYS(0, 0, stat("regular", &st)); birth = st.st_ctim; - sleep(1); + sleep(2); ASSERT_SYS(0, 3, open("regular", O_RDWR | O_TRUNC)); ASSERT_SYS(0, 0, fstat(3, &st)); EXPECT_EQ(1, timespec_cmp(st.st_ctim, birth)); @@ -487,7 +486,7 @@ TEST(open, mereOpen_doesntTouch) { ASSERT_SYS(0, 0, touch("regular", 0755)); ASSERT_SYS(0, 0, stat("regular", &st)); birth = st.st_ctim; - sleep(1); + sleep(2); ASSERT_SYS(0, 3, open("regular", O_RDWR)); ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, stat("regular", &st)); diff --git a/test/libc/calls/openatemp_test.c b/test/libc/calls/openatemp_test.c index 75639dc8f..ca328b41d 100644 --- a/test/libc/calls/openatemp_test.c +++ b/test/libc/calls/openatemp_test.c @@ -30,7 +30,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(openatemp, test) { char path[] = "foo.XXXXXX"; diff --git a/test/libc/calls/openbsd_test.c b/test/libc/calls/openbsd_test.c index 4952c4071..8d5dd0c29 100644 --- a/test/libc/calls/openbsd_test.c +++ b/test/libc/calls/openbsd_test.c @@ -25,8 +25,6 @@ #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void CheckPlatform(void) { if (IsOpenbsd()) return; // openbsd is ok if (IsLinux() && __is_linux_2_6_23()) return; // non-ancient linux is ok @@ -34,6 +32,10 @@ void CheckPlatform(void) { exit(0); } +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + void SetUp(void) { CheckPlatform(); } diff --git a/test/libc/calls/pledge2_test.c b/test/libc/calls/pledge2_test.c index c59ad7b19..98fe6e43b 100644 --- a/test/libc/calls/pledge2_test.c +++ b/test/libc/calls/pledge2_test.c @@ -78,7 +78,7 @@ TEST(pledge, testLogMessage_inSoftyMode) { ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); EXITS(0); close(fds[1]); - read(fds[0], msg, sizeof(msg)); + read(fds[0], msg, sizeof(msg) - 1); close(fds[0]); if (IsLinux()) { ASSERT_STARTSWITH("error: protected syscall socket", msg); diff --git a/test/libc/calls/pledge_test.c b/test/libc/calls/pledge_test.c index 1971ed6c6..7283225fe 100644 --- a/test/libc/calls/pledge_test.c +++ b/test/libc/calls/pledge_test.c @@ -26,6 +26,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -55,12 +56,14 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/time/time.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void OnSig(int sig) { // do nothing @@ -69,9 +72,8 @@ void OnSig(int sig) { int sys_memfd_secret(unsigned int); // our ENOSYS threshold void SetUp(void) { - __enable_threads(); if (pledge(0, 0) == -1) { - fprintf(stderr, "warning: pledge() not supported on this system\n"); + fprintf(stderr, "warning: pledge() not supported on this system %m\n"); exit(0); } testlib_extract("/zip/life.elf", "life.elf", 0755); @@ -115,7 +117,10 @@ TEST(pledge, execpromises_notok) { EXPECT_EQ(129, WEXITSTATUS(ws)); } -int Enclave(void *arg, int tid) { +void *Enclave(void *arg) { + sigset_t ss; + sigfillset(&ss); + sigprocmask(SIG_BLOCK, &ss, 0); ASSERT_SYS(0, 0, pledge("", 0)); int *job = arg; // get job job[0] = job[0] + job[1]; // do work @@ -124,11 +129,11 @@ int Enclave(void *arg, int tid) { TEST(pledge, withThreadMemory) { if (IsOpenbsd()) return; // openbsd doesn't allow it, wisely - struct spawn worker; - int job[2] = {2, 2}; // create workload - ASSERT_SYS(0, 0, _spawn(Enclave, job, &worker)); // create worker - ASSERT_SYS(0, 0, _join(&worker)); // wait for exit - EXPECT_EQ(4, job[0]); // check result + pthread_t worker; + int job[2] = {2, 2}; // create workload + ASSERT_EQ(0, pthread_create(&worker, 0, Enclave, job)); // create worker + ASSERT_EQ(0, pthread_join(worker, 0)); // wait for exit + EXPECT_EQ(4, job[0]); // check result } bool gotusr1; @@ -137,7 +142,7 @@ void OnUsr1(int sig) { gotusr1 = true; } -int TgkillWorker(void *arg, int tid) { +void *TgkillWorker(void *arg) { sigset_t mask; signal(SIGUSR1, OnUsr1); sigemptyset(&mask); @@ -150,15 +155,17 @@ TEST(pledge, tgkill) { // https://github.com/jart/cosmopolitan/issues/628 if (!IsLinux()) return; sigset_t mask; - struct spawn worker; + pthread_t worker; SPAWN(fork); sigemptyset(&mask); sigaddset(&mask, SIGUSR1); sigprocmask(SIG_BLOCK, &mask, 0); ASSERT_SYS(0, 0, pledge("stdio", 0)); - ASSERT_SYS(0, 0, _spawn(TgkillWorker, 0, &worker)); - ASSERT_SYS(0, 0, tgkill(getpid(), worker.ptid, SIGUSR1)); - ASSERT_SYS(0, 0, _join(&worker)); + ASSERT_SYS(0, 0, pthread_create(&worker, 0, TgkillWorker, 0)); + ASSERT_SYS(0, 0, + sys_tgkill(getpid(), _pthread_tid((struct PosixThread *)worker), + SIGUSR1)); + ASSERT_SYS(0, 0, pthread_join(worker, 0)); EXITS(0); } @@ -602,7 +609,7 @@ TEST(pledge_openbsd, bigSyscalls) { EXPECT_EQ(0, WEXITSTATUS(ws)); } -int LockWorker(void *arg, int tid) { +void *LockWorker(void *arg) { flockfile(stdout); ASSERT_EQ(gettid(), stdout->lock._owner); funlockfile(stdout); @@ -610,14 +617,13 @@ int LockWorker(void *arg, int tid) { } TEST(pledge, threadWithLocks_canCodeMorph) { - struct spawn worker; + pthread_t worker; int ws; // not sure how this works on OpenBSD but it works! if (!fork()) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio", 0)); - ASSERT_SYS(0, 0, _spawn(LockWorker, 0, &worker)); - ASSERT_SYS(0, 0, _join(&worker)); + ASSERT_EQ(0, pthread_create(&worker, 0, LockWorker, 0)); + ASSERT_EQ(0, pthread_join(worker, 0)); _Exit(0); } EXPECT_NE(-1, wait(&ws)); diff --git a/test/libc/calls/posix_fadvise_test.c b/test/libc/calls/posix_fadvise_test.c index 152a47856..43f184bc2 100644 --- a/test/libc/calls/posix_fadvise_test.c +++ b/test/libc/calls/posix_fadvise_test.c @@ -26,7 +26,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp(void) { if (IsOpenbsd() || IsXnu()) exit(0); diff --git a/test/libc/calls/pread_test.c b/test/libc/calls/pread_test.c index a3e00512a..9d809b64e 100644 --- a/test/libc/calls/pread_test.c +++ b/test/libc/calls/pread_test.c @@ -22,9 +22,9 @@ int fd; char buf[8]; -char testlib_enable_tmp_setup_teardown; void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/preadv_test.c b/test/libc/calls/preadv_test.c index 2a74a62b1..f2af96e8e 100644 --- a/test/libc/calls/preadv_test.c +++ b/test/libc/calls/preadv_test.c @@ -25,7 +25,9 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(preadv, ebadf) { EXPECT_SYS(EBADF, -1, preadv(-1, 0, 0, 0)); diff --git a/test/libc/calls/raise_race_test.c b/test/libc/calls/raise_race_test.c deleted file mode 100644 index a1c178231..000000000 --- a/test/libc/calls/raise_race_test.c +++ /dev/null @@ -1,117 +0,0 @@ -/*-*- 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│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ │ -│ libc-test │ -│ Copyright © 2005-2013 libc-test AUTHORS │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ -│ │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" -#include "libc/calls/calls.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/sig.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" - -// libc-test/src/regression/raise-race.c -// commit: 370f78f2c80c64b7b0780a01e672494a26b5678e 2011-03-09 -// commit: 0bed7e0acfd34e3fb63ca0e4d99b7592571355a9 2011-03-09 -// raise should be robust against async fork in a signal handler -// [jart] i can't believe fork() is async-signal-safe - -#define t_error(...) \ - do { \ - kprintf(__VA_ARGS__); \ - ++t_status; \ - } while (0) - -static atomic_int c0; -static atomic_int c1; -static atomic_int child; -static atomic_int t_status; - -static void handler0(int sig) { - c0++; -} - -static void handler1(int sig) { - c1++; - switch (fork()) { - case 0: - child = 1; - break; - case -1: - t_error("fork failed: %s\n", strerror(errno)); - default: - break; - } -} - -static void *start(void *arg) { - int i, r, s; - for (i = 0; i < 1000; i++) { - r = raise(SIGRTMIN); - if (r) t_error("raise failed: %s\n", strerror(errno)); - } - if (c0 != 1000) { - t_error("lost signals: got %d, wanted 1000 (ischild %d forks %d)\n", c0, - child, c1); - } - if (child) _exit(t_status); - /* make sure we got all pthread_kills, then wait the forked children */ - while (c1 < 100) donothing; - for (i = 0; i < 100; i++) { - r = wait(&s); - if (r == -1) { - t_error("wait failed: %s\n", strerror(errno)); - } else if (!WIFEXITED(s) || WTERMSIG(s)) { - t_error("child failed: pid:%d status:%d sig:%s\n", r, s, - strsignal(WTERMSIG(s))); - } - } - return 0; -} - -TEST(raise, test) { - if (IsNetbsd()) return; // why doesn't it work? - if (IsOpenbsd()) return; // no support for realtime signals yet - if (IsXnu()) return; // no support for realtime signals yet - if (IsWindows()) return; // TODO(jart): why does it exit 128+SIGRTMIN? - void *p; - int r, i; - pthread_t t; - if (signal(SIGRTMIN, handler0) == SIG_ERR) - t_error("registering signal handler failed: %s\n", strerror(errno)); - if (signal(SIGRTMIN + 1, handler1) == SIG_ERR) - t_error("registering signal handler failed: %s\n", strerror(errno)); - r = pthread_create(&t, 0, start, 0); - if (r) t_error("pthread_create failed: %s\n", strerror(r)); - for (i = 0; i < 100; i++) { - r = pthread_kill(t, SIGRTMIN + 1); - if (r) t_error("phread_kill failed: %s\n", strerror(r)); - } - r = pthread_join(t, &p); - if (r) t_error("pthread_join failed: %s\n", strerror(r)); - ASSERT_EQ(0, t_status); -} diff --git a/test/libc/calls/raise_test.c b/test/libc/calls/raise_test.c index b85932370..60b470976 100644 --- a/test/libc/calls/raise_test.c +++ b/test/libc/calls/raise_test.c @@ -26,7 +26,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" TEST(raise, trap) { signal(SIGTRAP, SIG_DFL); @@ -58,17 +58,17 @@ void WorkerQuit(int sig, siginfo_t *si, void *ctx) { ASSERT_EQ(threadid, gettid()); } -int Worker(void *arg, int tid) { +void *Worker(void *arg) { struct sigaction sa = {.sa_sigaction = WorkerQuit, .sa_flags = SA_SIGINFO}; ASSERT_EQ(0, sigaction(SIGILL, &sa, 0)); - threadid = tid; + threadid = gettid(); ASSERT_EQ(0, raise(SIGILL)); return 0; } TEST(raise, threaded) { signal(SIGILL, SIG_DFL); - struct spawn worker; - ASSERT_SYS(0, 0, _spawn(Worker, 0, &worker)); - ASSERT_SYS(0, 0, _join(&worker)); + pthread_t worker; + ASSERT_EQ(0, pthread_create(&worker, 0, Worker, 0)); + ASSERT_EQ(0, pthread_join(worker, 0)); } diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index f147352f1..e0f67d551 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -33,7 +33,9 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(read, eof) { char b[8] = "hello"; diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index 2adbf57a1..a8f63824d 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -32,9 +32,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/renameat_test.c b/test/libc/calls/renameat_test.c index 44c8554cc..478725999 100644 --- a/test/libc/calls/renameat_test.c +++ b/test/libc/calls/renameat_test.c @@ -24,9 +24,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/reservefd_test.c b/test/libc/calls/reservefd_test.c index 360e0188a..84def88c6 100644 --- a/test/libc/calls/reservefd_test.c +++ b/test/libc/calls/reservefd_test.c @@ -39,9 +39,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" @@ -55,7 +53,6 @@ void SetUpOnce(void) { close(i); } errno = 0; - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio rpath", 0)); } diff --git a/test/libc/calls/sched_getaffinity_test.c b/test/libc/calls/sched_getaffinity_test.c index 1c20849ea..840c54873 100644 --- a/test/libc/calls/sched_getaffinity_test.c +++ b/test/libc/calls/sched_getaffinity_test.c @@ -24,7 +24,7 @@ #include "libc/intrin/popcnt.h" #include "libc/intrin/safemacros.internal.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" diff --git a/test/libc/calls/setrlimit_test.c b/test/libc/calls/setrlimit_test.c index ed89b9c31..782a5c328 100644 --- a/test/libc/calls/setrlimit_test.c +++ b/test/libc/calls/setrlimit_test.c @@ -19,6 +19,7 @@ #include "dsp/core/core.h" #include "libc/calls/calls.h" #include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/timespec.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/directmap.internal.h" @@ -57,24 +58,25 @@ void OnSigxfsz(int sig) { TEST(setrlimit, testCpuLimit) { int wstatus; - long double start; struct rlimit rlim; + struct timespec start; double matrices[3][3][3]; - if (IsWindows()) return; /* of course it doesn't work on windows */ - if (IsOpenbsd()) return; /* TODO(jart): fix flake */ + if (IsWindows()) return; // of course it doesn't work on windows + if (IsXnu()) return; // TODO(jart): it worked before + if (IsOpenbsd()) return; // TODO(jart): fix flake ASSERT_NE(-1, (wstatus = xspawn(0))); if (wstatus == -2) { ASSERT_EQ(0, xsigaction(SIGXCPU, OnSigxcpu, 0, 0, 0)); ASSERT_EQ(0, getrlimit(RLIMIT_CPU, &rlim)); - rlim.rlim_cur = 1; /* set soft limit to one second */ + rlim.rlim_cur = 1; // set soft limit to one second ASSERT_EQ(0, setrlimit(RLIMIT_CPU, &rlim)); - start = nowl(); + start = timespec_real(); do { matmul3(matrices[0], matrices[1], matrices[2]); matmul3(matrices[0], matrices[1], matrices[2]); matmul3(matrices[0], matrices[1], matrices[2]); matmul3(matrices[0], matrices[1], matrices[2]); - } while ((nowl() - start) < 5); + } while (timespec_sub(timespec_real(), start).tv_sec < 5); _Exit(1); } EXPECT_TRUE(WIFEXITED(wstatus)); diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 2320f28d2..9419821eb 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -29,7 +29,6 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/nexgen32e/vendor.internal.h" #include "libc/runtime/internal.h" @@ -39,14 +38,15 @@ #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/uc.h" +#include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" #include "third_party/xed/x86.h" struct sigaction oldsa; volatile bool gotsigint; void SetUpOnce(void) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio rpath proc", 0)); } @@ -59,6 +59,12 @@ void SetUp(void) { gotsigint = false; } +void TearDown(void) { + sigset_t ss; + sigprocmask(SIG_SETMASK, 0, &ss); + ASSERT_TRUE(sigisemptyset(&ss)); +} + //////////////////////////////////////////////////////////////////////////////// // test that signal handlers expose cpu state, and let it be changed arbitrarily @@ -158,7 +164,7 @@ TEST(sigaction, testPingPongParentChildWithSigint) { EXPECT_EQ(0, WEXITSTATUS(status)); EXPECT_EQ(0, WTERMSIG(status)); EXPECT_SYS(0, 0, sigaction(SIGINT, &oldint, 0)); - EXPECT_SYS(0, 0, sigprocmask(SIG_BLOCK, &oldmask, 0)); + EXPECT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldmask, 0)); } #ifdef __x86_64__ @@ -225,14 +231,14 @@ void OnSignal(int sig, siginfo_t *si, void *ctx) { TEST(sigaction, ignoringSignalDiscardsSignal) { struct sigaction sa = {.sa_sigaction = OnSignal, .sa_flags = SA_SIGINFO}; ASSERT_EQ(0, sigaction(SIGUSR1, &sa, NULL)); - sigset_t blocked; + sigset_t blocked, oldmask; sigemptyset(&blocked); sigaddset(&blocked, SIGUSR1); - ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &blocked, NULL)); + ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &blocked, &oldmask)); ASSERT_EQ(0, raise(SIGUSR1)); ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_IGN)); ASSERT_EQ(0, sigaction(SIGUSR1, &sa, NULL)); - ASSERT_EQ(0, sigprocmask(SIG_UNBLOCK, &blocked, NULL)); + ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &oldmask, NULL)); EXPECT_EQ(0, OnSignalCnt); } @@ -348,3 +354,51 @@ TEST(sigaction, NoDefer) { raise(SIGUSR2); ASSERT_SYS(0, 0, sigaction(SIGUSR2, &os, 0)); } + +int *segfaults; + +void OnSegfault(int sig) { + if (++*segfaults == 10000) { + _Exit(0); + } +} + +dontubsan dontasan int Segfault(char *p) { + return *p; +} + +int (*pSegfault)(char *) = Segfault; + +TEST(sigaction, returnFromSegvHandler_loopsForever) { + if (IsXnu()) return; // seems busted + segfaults = _mapshared(sizeof(*segfaults)); + SPAWN(fork); + signal(SIGSEGV, OnSegfault); + _Exit(pSegfault(0)); + EXITS(0); + ASSERT_EQ(10000, *segfaults); + munmap(segfaults, sizeof(*segfaults)); +} + +TEST(sigaction, ignoreSigSegv_notPossible) { + if (IsXnu()) return; // seems busted + SPAWN(fork); + signal(SIGSEGV, SIG_IGN); + _Exit(pSegfault(0)); + TERMS(SIGSEGV); +} + +TEST(sigaction, killSigSegv_canBeIgnored) { + int child, ws; + if (IsWindows()) return; // TODO + sighandler_t old = signal(SIGSEGV, SIG_IGN); + ASSERT_NE(-1, (child = fork())); + while (!child) { + pause(); + } + ASSERT_SYS(0, 0, kill(child, SIGSEGV)); + EXPECT_SYS(0, 0, kill(child, SIGTERM)); + EXPECT_SYS(0, child, wait(&ws)); + EXPECT_EQ(SIGTERM, ws); + signal(SIGSEGV, old); +} diff --git a/test/libc/calls/sigprocmask_test.c b/test/libc/calls/sigprocmask_test.c index cd8502fc5..a4041e5aa 100644 --- a/test/libc/calls/sigprocmask_test.c +++ b/test/libc/calls/sigprocmask_test.c @@ -16,12 +16,17 @@ │ 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/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" @@ -36,6 +41,13 @@ void OnSig(int sig, siginfo_t *si, void *ctx) { ++n; } +const char *DescribeMask(void) { + sigset_t ss; + _Thread_local static char buf[128]; + unassert(!sigprocmask(SIG_SETMASK, 0, &ss)); + return (DescribeSigset)(buf, 0, &ss); +} + TEST(sigprocmask, testMultipleBlockedDeliveries) { sigset_t neu, old; struct sigaction oldusr1, oldusr2; diff --git a/test/libc/calls/splice_test.c b/test/libc/calls/splice_test.c index c09d8aa05..46476192f 100644 --- a/test/libc/calls/splice_test.c +++ b/test/libc/calls/splice_test.c @@ -23,7 +23,9 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp(void) { int e = errno; diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index af38ebdc7..d21c644c2 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -37,7 +37,9 @@ __static_yoink("zipos"); -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(stat_010, testEmptyFile_sizeIsZero) { struct stat st; diff --git a/test/libc/calls/statfs_test.c b/test/libc/calls/statfs_test.c index 24b6d4167..bc3c61602 100644 --- a/test/libc/calls/statfs_test.c +++ b/test/libc/calls/statfs_test.c @@ -22,9 +22,12 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; struct statfs f; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + TEST(statfs, testFile) { EXPECT_SYS(0, 0, touch("foo", 0644)); EXPECT_SYS(0, 0, statfs("foo", &f)); diff --git a/test/libc/calls/symlinkat_test.c b/test/libc/calls/symlinkat_test.c index 1c1ae1437..918d15886 100644 --- a/test/libc/calls/symlinkat_test.c +++ b/test/libc/calls/symlinkat_test.c @@ -28,11 +28,11 @@ #include "libc/sysv/consts/s.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; char p[2][PATH_MAX]; struct stat st; void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index 2d3029011..ead227dac 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -32,25 +32,26 @@ TEST_LIBC_CALLS_CHECKS = \ TEST_LIBC_CALLS_DIRECTDEPS = \ DSP_CORE \ LIBC_CALLS \ - LIBC_TINYMATH \ - LIBC_SOCK \ LIBC_FMT \ LIBC_INTRIN \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ - LIBC_STDIO \ LIBC_NT_KERNEL32 \ - LIBC_SYSV_CALLS \ + LIBC_PROC \ LIBC_RUNTIME \ + LIBC_SOCK \ + LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ + LIBC_SYSV_CALLS \ + LIBC_TESTLIB \ LIBC_THREAD \ LIBC_TIME \ - LIBC_TESTLIB \ + LIBC_TINYMATH \ LIBC_X \ - TOOL_DECODE_LIB \ THIRD_PARTY_COMPILER_RT \ + TOOL_DECODE_LIB \ THIRD_PARTY_XED TEST_LIBC_CALLS_DEPS := \ diff --git a/test/libc/calls/unlinkat_test.c b/test/libc/calls/unlinkat_test.c index 328eb7ec8..c45d74444 100644 --- a/test/libc/calls/unlinkat_test.c +++ b/test/libc/calls/unlinkat_test.c @@ -23,9 +23,8 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/unveil_test.c b/test/libc/calls/unveil_test.c index 275794f1a..7f8610183 100644 --- a/test/libc/calls/unveil_test.c +++ b/test/libc/calls/unveil_test.c @@ -40,14 +40,12 @@ #include "libc/sysv/consts/sock.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" #include "libc/x/x.h" #include "libc/x/xasprintf.h" #define EACCES_OR_ENOENT (IsOpenbsd() ? ENOENT : EACCES) -char testlib_enable_tmp_setup_teardown; - struct stat st; bool HasUnveilSupport(void) { @@ -60,11 +58,11 @@ bool UnveilCanSecureTruncate(void) { } void SetUpOnce(void) { - __enable_threads(); if (!HasUnveilSupport()) { fprintf(stderr, "warning: unveil() not supported on this system: %m\n"); exit(0); } + testlib_enable_tmp_setup_teardown(); } void SetUp(void) { @@ -284,25 +282,25 @@ TEST(unveil, procfs_isForbiddenByDefault) { EXITS(0); } -int Worker(void *arg, int tid) { +void *Worker(void *arg) { ASSERT_SYS(EACCES_OR_ENOENT, -1, open("garden/secret.txt", O_RDONLY)); return 0; } TEST(unveil, isInheritedAcrossThreads) { - struct spawn t; + pthread_t t; SPAWN(fork); ASSERT_SYS(0, 0, mkdir("jail", 0755)); ASSERT_SYS(0, 0, mkdir("garden", 0755)); ASSERT_SYS(0, 0, xbarf("garden/secret.txt", "hello", 5)); ASSERT_SYS(0, 0, unveil("jail", "rw")); ASSERT_SYS(0, 0, unveil(0, 0)); - ASSERT_SYS(0, 0, _spawn(Worker, 0, &t)); - EXPECT_SYS(0, 0, _join(&t)); + ASSERT_SYS(0, 0, pthread_create(&t, 0, Worker, 0)); + EXPECT_SYS(0, 0, pthread_join(t, 0)); EXITS(0); } -int Worker2(void *arg, int tid) { +void *Worker2(void *arg) { ASSERT_SYS(0, 0, unveil("jail", "rw")); ASSERT_SYS(0, 0, unveil(0, 0)); ASSERT_SYS(EACCES_OR_ENOENT, -1, open("garden/secret.txt", O_RDONLY)); @@ -310,13 +308,13 @@ int Worker2(void *arg, int tid) { } TEST(unveil, isThreadSpecificOnLinux_isProcessWideOnOpenbsd) { - struct spawn t; + pthread_t t; SPAWN(fork); ASSERT_SYS(0, 0, mkdir("jail", 0755)); ASSERT_SYS(0, 0, mkdir("garden", 0755)); ASSERT_SYS(0, 0, xbarf("garden/secret.txt", "hello", 5)); - ASSERT_SYS(0, 0, _spawn(Worker2, 0, &t)); - EXPECT_SYS(0, 0, _join(&t)); + ASSERT_SYS(0, 0, pthread_create(&t, 0, Worker2, 0)); + EXPECT_SYS(0, 0, pthread_join(t, 0)); if (IsOpenbsd()) { ASSERT_SYS(ENOENT, -1, open("garden/secret.txt", O_RDONLY)); } else { diff --git a/test/libc/calls/utimensat_test.c b/test/libc/calls/utimensat_test.c index 3092b7151..72ff6d5b2 100644 --- a/test/libc/calls/utimensat_test.c +++ b/test/libc/calls/utimensat_test.c @@ -31,9 +31,8 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/calls/vfork_test.c b/test/libc/calls/vfork_test.c index 685a4ee1e..697abebf1 100644 --- a/test/libc/calls/vfork_test.c +++ b/test/libc/calls/vfork_test.c @@ -23,7 +23,9 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(vfork, test) { int fd; diff --git a/test/libc/calls/write_test.c b/test/libc/calls/write_test.c index 6d0e9c321..bfb293a15 100644 --- a/test/libc/calls/write_test.c +++ b/test/libc/calls/write_test.c @@ -28,6 +28,8 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/log/backtrace.internal.h" +#include "libc/runtime/runtime.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" @@ -37,7 +39,18 @@ #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + +TEST(write, advancesFilePointer) { + ASSERT_SYS(0, 3, creat("foo", 0644)); + ASSERT_SYS(0, 1, write(3, "x", 1)); + ASSERT_SYS(0, 1, lseek(3, 0, SEEK_CUR)); + ASSERT_SYS(0, 1, write(3, "y", 1)); + ASSERT_SYS(0, 2, lseek(3, 0, SEEK_CUR)); + ASSERT_SYS(0, 0, close(3)); +} TEST(write, notOpen_ebadf) { ASSERT_SYS(EBADF, -1, write(-1, 0, 0)); @@ -108,6 +121,15 @@ TEST(write, rlimitFsizeExceeded_raisesEfbig) { EXITS(0); } +TEST(pwrite, testWritePastEof_extendsFile) { + char buf[8] = {0}; + EXPECT_SYS(0, 3, creat("foo", 0644)); + EXPECT_SYS(0, 8, pwrite(3, buf, 8, 100)); + EXPECT_EQ(0, lseek(3, 0, SEEK_CUR)); + EXPECT_EQ(108, lseek(3, 0, SEEK_END)); + EXPECT_SYS(0, 0, close(3)); +} + BENCH(write, bench) { ASSERT_SYS(0, 3, open("/dev/null", O_WRONLY)); EZBENCH2("write", donothing, write(3, "hello", 5)); diff --git a/test/libc/calls/writev_test.c b/test/libc/calls/writev_test.c index 30734acb1..2680ed7f2 100644 --- a/test/libc/calls/writev_test.c +++ b/test/libc/calls/writev_test.c @@ -32,9 +32,8 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } diff --git a/test/libc/dns/prototxt_test.c b/test/libc/dns/prototxt_test.c index f2c05a127..ad3e9cc6f 100644 --- a/test/libc/dns/prototxt_test.c +++ b/test/libc/dns/prototxt_test.c @@ -25,15 +25,17 @@ │ OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dns/prototxt.h" #include "libc/calls/calls.h" #include "libc/dns/dns.h" #include "libc/dns/ent.h" -#include "libc/dns/prototxt.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp() { int fd; diff --git a/test/libc/dns/servicestxt_test.c b/test/libc/dns/servicestxt_test.c index a3e08c095..5d6a27bbc 100644 --- a/test/libc/dns/servicestxt_test.c +++ b/test/libc/dns/servicestxt_test.c @@ -32,7 +32,9 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp() { int fd; diff --git a/test/libc/fmt/basename_test.c b/test/libc/fmt/basename_test.c index 153177d18..3dff16909 100644 --- a/test/libc/fmt/basename_test.c +++ b/test/libc/fmt/basename_test.c @@ -17,12 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/libgen.h" -#include "libc/intrin/bits.h" -#include "libc/mem/gc.internal.h" -#include "libc/mem/mem.h" +#include "libc/str/str.h" #include "libc/testlib/testlib.h" -#define BASENAME(x) basename(gc(strdup(x))) +static char dup[128]; + +#define BASENAME(x) basename(strcpy(dup, x)) TEST(basename, testRegularExamples) { EXPECT_STREQ("lib", BASENAME("/usr/lib")); diff --git a/test/libc/intrin/getenv_test.c b/test/libc/intrin/getenv_test.c index 5ef0c98df..b6362d80d 100644 --- a/test/libc/intrin/getenv_test.c +++ b/test/libc/intrin/getenv_test.c @@ -21,6 +21,8 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +__static_yoink("malloc"); + TEST(getenv, test) { putenv("X=y"); EXPECT_STREQ("y", getenv("X")); diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index 23d21e365..7dbb86daf 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -216,7 +216,7 @@ TEST(ksnprintf, testSymbols) { if (hassymbols) { ASSERT_STREQ("&strlen", b[0]); } else { - ksnprintf(b[1], 32, "&%x", strlen); + ksnprintf(b[1], 32, "&%lx", strlen); ASSERT_STREQ(b[1], b[0]); } } diff --git a/test/libc/intrin/lockipc_test.c b/test/libc/intrin/lockipc_test.c index daac676ff..ac2288c87 100644 --- a/test/libc/intrin/lockipc_test.c +++ b/test/libc/intrin/lockipc_test.c @@ -70,7 +70,7 @@ TEST(lockipc, mutex) { // wait for processes to finish for (;;) { e = errno; - if ((pid = waitpid(0, &ws, 0)) != -1) { + if ((pid = waitpid(-1, &ws, 0)) != -1) { if (WIFSIGNALED(ws)) { kprintf("process %d terminated with %G\n", pid, WTERMSIG(ws)); testlib_incrementfailed(); diff --git a/test/libc/intrin/pthread_mutex_lock2_test.c b/test/libc/intrin/pthread_mutex_lock2_test.c index 85a249787..4c3208b21 100644 --- a/test/libc/intrin/pthread_mutex_lock2_test.c +++ b/test/libc/intrin/pthread_mutex_lock2_test.c @@ -26,7 +26,6 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #include "third_party/nsync/mu.h" @@ -63,7 +62,7 @@ FIXTURE(pthread_mutex_lock, errorcheck) { //////////////////////////////////////////////////////////////////////////////// // TESTS -int MutexWorker(void *p, int tid) { +void *MutexWorker(void *p) { int i; ++started; for (i = 0; i < ITERATIONS; ++i) { @@ -78,7 +77,7 @@ int MutexWorker(void *p, int tid) { TEST(pthread_mutex_lock, contention) { int i; - struct spawn *th = gc(malloc(sizeof(struct spawn) * THREADS)); + pthread_t *th = gc(malloc(sizeof(pthread_t) * THREADS)); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); pthread_mutex_init(&lock, &attr); @@ -87,10 +86,10 @@ TEST(pthread_mutex_lock, contention) { started = 0; finished = 0; for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _spawn(MutexWorker, (void *)(intptr_t)i, th + i)); + ASSERT_EQ(0, pthread_create(th + i, 0, MutexWorker, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _join(th + i)); + ASSERT_EQ(0, pthread_join(th[i], 0)); } EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); @@ -157,7 +156,7 @@ struct SpinContentionArgs { atomic_char ready; }; -int SpinContentionWorker(void *arg, int tid) { +void *SpinContentionWorker(void *arg) { struct SpinContentionArgs *a = arg; while (!atomic_load_explicit(&a->done, memory_order_relaxed)) { pthread_spin_lock(a->spin); @@ -173,7 +172,7 @@ struct MutexContentionArgs { atomic_char ready; }; -int MutexContentionWorker(void *arg, int tid) { +void *MutexContentionWorker(void *arg) { struct MutexContentionArgs *a = arg; while (!atomic_load_explicit(&a->done, memory_order_relaxed)) { if (pthread_mutex_lock(a->mutex)) notpossible; @@ -189,7 +188,7 @@ struct NsyncContentionArgs { atomic_char ready; }; -int NsyncContentionWorker(void *arg, int tid) { +void *NsyncContentionWorker(void *arg) { struct NsyncContentionArgs *a = arg; while (!atomic_load_explicit(&a->done, memory_order_relaxed)) { nsync_mu_lock(a->nsync); @@ -200,24 +199,24 @@ int NsyncContentionWorker(void *arg, int tid) { } BENCH(pthread_mutex_lock, bench_contended) { - struct spawn t; + pthread_t t; { pthread_spinlock_t s = {0}; struct SpinContentionArgs a = {&s}; - _spawn(SpinContentionWorker, &a, &t); + pthread_create(&t, 0, SpinContentionWorker, &a); while (!a.ready) sched_yield(); EZBENCH2("spin 2x", donothing, BenchSpinUnspin(&s)); a.done = true; - _join(&t); + pthread_join(t, 0); } { nsync_mu m = {0}; struct NsyncContentionArgs a = {&m}; - _spawn(NsyncContentionWorker, &a, &t); + pthread_create(&t, 0, NsyncContentionWorker, &a); while (!a.ready) sched_yield(); EZBENCH2("nsync 2x", donothing, BenchLockUnlockNsync(&m)); a.done = true; - _join(&t); + pthread_join(t, 0); } { pthread_mutex_t m; @@ -226,11 +225,11 @@ BENCH(pthread_mutex_lock, bench_contended) { pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); pthread_mutex_init(&m, &attr); struct MutexContentionArgs a = {&m}; - _spawn(MutexContentionWorker, &a, &t); + pthread_create(&t, 0, MutexContentionWorker, &a); while (!a.ready) sched_yield(); EZBENCH2("normal 2x", donothing, BenchLockUnlock(&m)); a.done = true; - _join(&t); + pthread_join(t, 0); } { pthread_mutex_t m; @@ -239,11 +238,11 @@ BENCH(pthread_mutex_lock, bench_contended) { pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&m, &attr); struct MutexContentionArgs a = {&m}; - _spawn(MutexContentionWorker, &a, &t); + pthread_create(&t, 0, MutexContentionWorker, &a); while (!a.ready) sched_yield(); EZBENCH2("recursive 2x", donothing, BenchLockUnlock(&m)); a.done = true; - _join(&t); + pthread_join(t, 0); } { pthread_mutex_t m; @@ -252,10 +251,10 @@ BENCH(pthread_mutex_lock, bench_contended) { pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); pthread_mutex_init(&m, &attr); struct MutexContentionArgs a = {&m}; - _spawn(MutexContentionWorker, &a, &t); + pthread_create(&t, 0, MutexContentionWorker, &a); while (!a.ready) sched_yield(); EZBENCH2("errorcheck 2x", donothing, BenchLockUnlock(&m)); a.done = true; - _join(&t); + pthread_join(t, 0); } } diff --git a/test/libc/intrin/pthread_mutex_lock_test.c b/test/libc/intrin/pthread_mutex_lock_test.c index 5402c9ab2..86ea85aad 100644 --- a/test/libc/intrin/pthread_mutex_lock_test.c +++ b/test/libc/intrin/pthread_mutex_lock_test.c @@ -36,10 +36,8 @@ #include "libc/sysv/consts/rlimit.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #include "third_party/nsync/mu.h" #define THREADS 8 @@ -50,10 +48,9 @@ atomic_int started; atomic_int finished; pthread_mutex_t mylock; pthread_spinlock_t slock; -struct spawn th[THREADS]; +pthread_t th[THREADS]; void SetUpOnce(void) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio rpath", 0)); } @@ -107,7 +104,7 @@ TEST(pthread_mutex_lock, errorcheck) { ASSERT_EQ(0, pthread_mutex_destroy(&lock)); } -int MutexWorker(void *p, int tid) { +void *MutexWorker(void *p) { int i; ++started; for (i = 0; i < ITERATIONS; ++i) { @@ -132,10 +129,11 @@ TEST(pthread_mutex_lock, contention) { started = 0; finished = 0; for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _spawn(MutexWorker, (void *)(intptr_t)i, th + i)); + ASSERT_SYS(0, 0, + pthread_create(th + i, 0, MutexWorker, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _join(th + i)); + ASSERT_SYS(0, 0, pthread_join(th[i], 0)); } EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); @@ -154,10 +152,10 @@ TEST(pthread_mutex_lock, rcontention) { started = 0; finished = 0; for (i = 0; i < THREADS; ++i) { - ASSERT_NE(-1, _spawn(MutexWorker, (void *)(intptr_t)i, th + i)); + ASSERT_EQ(0, pthread_create(th + i, 0, MutexWorker, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - _join(th + i); + ASSERT_EQ(0, pthread_join(th[i], 0)); } EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); @@ -176,10 +174,10 @@ TEST(pthread_mutex_lock, econtention) { started = 0; finished = 0; for (i = 0; i < THREADS; ++i) { - ASSERT_NE(-1, _spawn(MutexWorker, (void *)(intptr_t)i, th + i)); + ASSERT_NE(-1, pthread_create(th + i, 0, MutexWorker, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - _join(th + i); + pthread_join(th[i], 0); } EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); @@ -187,7 +185,7 @@ TEST(pthread_mutex_lock, econtention) { EXPECT_EQ(0, pthread_mutex_destroy(&mylock)); } -int SpinlockWorker(void *p, int tid) { +void *SpinlockWorker(void *p) { int i; ++started; for (i = 0; i < ITERATIONS; ++i) { @@ -196,7 +194,7 @@ int SpinlockWorker(void *p, int tid) { pthread_spin_unlock(&slock); } ++finished; - STRACE("SpinlockWorker Finished %d", tid); + STRACE("SpinlockWorker Finished %d", gettid()); return 0; } @@ -213,10 +211,11 @@ TEST(pthread_spin_lock, test) { EXPECT_EQ(EBUSY, pthread_spin_trylock(&slock)); EXPECT_EQ(0, pthread_spin_unlock(&slock)); for (i = 0; i < THREADS; ++i) { - ASSERT_NE(-1, _spawn(SpinlockWorker, (void *)(intptr_t)i, th + i)); + ASSERT_EQ(0, + pthread_create(th + i, 0, SpinlockWorker, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - _join(th + i); + ASSERT_EQ(0, pthread_join(th[i], 0)); } EXPECT_EQ(THREADS, started); EXPECT_EQ(THREADS, finished); diff --git a/test/libc/intrin/rand64_test.c b/test/libc/intrin/rand64_test.c index 05c570599..f7d6310d1 100644 --- a/test/libc/intrin/rand64_test.c +++ b/test/libc/intrin/rand64_test.c @@ -25,7 +25,6 @@ #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #define THREADS 8 @@ -35,7 +34,6 @@ volatile uint64_t A[THREADS * ENTRIES]; pthread_barrier_t barrier; void SetUpOnce(void) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio", 0)); } @@ -47,7 +45,7 @@ dontinline void Generate(int i) { A[i] = _rand64(); } -int Thrasher(void *arg, int tid) { +void *Thrasher(void *arg) { int i, id = (intptr_t)arg; pthread_barrier_wait(&barrier); for (i = 0; i < ENTRIES; ++i) { @@ -74,8 +72,8 @@ TEST(_rand64, testLcg_doesntProduceIdenticalValues) { TEST(_rand64, testThreadSafety_doesntProduceIdenticalValues) { int i, j; sigset_t ss, oldss; + pthread_t th[THREADS]; struct sigaction oldsa; - struct spawn th[THREADS]; struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART}; EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa)); bzero((void *)A, sizeof(A)); @@ -84,10 +82,10 @@ TEST(_rand64, testThreadSafety_doesntProduceIdenticalValues) { EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, THREADS)); for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _spawn(Thrasher, (void *)(intptr_t)i, th + i)); + ASSERT_EQ(0, pthread_create(th + i, 0, Thrasher, (void *)(intptr_t)i)); } for (i = 0; i < THREADS; ++i) { - ASSERT_SYS(0, 0, _join(th + i)); + ASSERT_EQ(0, pthread_join(th[i], 0)); } sigaction(SIGCHLD, &oldsa, 0); sigprocmask(SIG_BLOCK, &oldss, 0); diff --git a/test/libc/intrin/test.mk b/test/libc/intrin/test.mk index 05d4cc7a8..96ad641b8 100644 --- a/test/libc/intrin/test.mk +++ b/test/libc/intrin/test.mk @@ -29,6 +29,7 @@ TEST_LIBC_INTRIN_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index e637bc233..f1d7a693c 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -44,9 +44,8 @@ __static_yoink("backtrace.com"); __static_yoink("backtrace.com.dbg"); -char testlib_enable_tmp_setup_teardown_once; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown_once(); ASSERT_NE(-1, mkdir("bin", 0755)); testlib_extract("/zip/backtrace.com", "bin/backtrace.com", 0755); testlib_extract("/zip/backtrace.com.dbg", "bin/backtrace.com.dbg", 0755); diff --git a/test/libc/mem/realpath_test.c b/test/libc/mem/realpath_test.c index a54b51472..56062d575 100644 --- a/test/libc/mem/realpath_test.c +++ b/test/libc/mem/realpath_test.c @@ -25,7 +25,9 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp(void) { touch("conftest.a", 0644); diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index 974c734e5..2a7b39753 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -35,6 +35,7 @@ TEST_LIBC_MEM_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/test/libc/nexgen32e/gclongjmp_test.c b/test/libc/nexgen32e/gclongjmp_test.c index eecc684f2..0fabe8e25 100644 --- a/test/libc/nexgen32e/gclongjmp_test.c +++ b/test/libc/nexgen32e/gclongjmp_test.c @@ -27,7 +27,7 @@ #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" #include "libc/x/x.h" #ifdef __x86_64__ // TODO(jart): get gclongjmp() working properly on aarch64 @@ -86,16 +86,16 @@ void crawl(const char *path) { crawl(_gc(xdirname(path))); } -int Worker(void *arg, int tid) { +void *Worker(void *arg) { crawl("a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d"); return 0; } TEST(gc, torture) { int i, n = 32; - struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); + for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0)); + for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, pthread_join(t[i], 0)); } void crawl2(jmp_buf jb, const char *path) { @@ -103,7 +103,7 @@ void crawl2(jmp_buf jb, const char *path) { crawl2(jb, _gc(xdirname(path))); } -int Worker2(void *arg, int tid) { +void *Worker2(void *arg) { jmp_buf jb; if (!setjmp(jb)) { crawl2(jb, "a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d/a/b/c/d"); @@ -113,9 +113,13 @@ int Worker2(void *arg, int tid) { TEST(_gclongjmp, torture) { int i, n = 32; - struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker2, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); + for (i = 0; i < n; ++i) { + ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker2, 0)); + } + for (i = 0; i < n; ++i) { + EXPECT_SYS(0, 0, pthread_join(t[i], 0)); + } } dontinline void F1(void) { diff --git a/test/libc/runtime/fork_test.c b/test/libc/proc/fork_test.c similarity index 100% rename from test/libc/runtime/fork_test.c rename to test/libc/proc/fork_test.c diff --git a/test/libc/proc/life-pe.com b/test/libc/proc/life-pe.com new file mode 100755 index 000000000..2cde9e250 Binary files /dev/null and b/test/libc/proc/life-pe.com differ diff --git a/test/libc/stdio/posix_spawn_test.c b/test/libc/proc/posix_spawn_test.c similarity index 78% rename from test/libc/stdio/posix_spawn_test.c rename to test/libc/proc/posix_spawn_test.c index bf999ec14..a9ad58283 100644 --- a/test/libc/stdio/posix_spawn_test.c +++ b/test/libc/proc/posix_spawn_test.c @@ -16,15 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #include "libc/assert.h" #include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" @@ -34,6 +38,7 @@ #include "libc/mem/gc.h" #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" +#include "libc/proc/proc.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" @@ -42,6 +47,8 @@ #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/rusage.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -67,7 +74,9 @@ const char kTinyLinuxExit[128] = { 0x6a, 0x2a, 0x5f, 0x6a, 0x3c, 0x58, 0x0f, 0x05, // j*_jsi_signo); + // TODO(jart): find a safer way to deliver signals on win32 + if (!IsWindows()) { + // TODO(jart): what's up with openbsd? + if (!IsOpenbsd()) { + EXPECT_EQ(CLD_EXITED, si->si_code); + EXPECT_EQ(sigchld_pid, si->si_pid); + } + EXPECT_EQ(getuid(), si->si_uid); + } + EXPECT_NE(NULL, ctx); + sigchld_got_signal = true; +} + +TEST(posix_spawn, sigchld) { + struct sigaction newsa, oldsa; + sigset_t oldmask, blocksigchld, unblockall; + char *prog = GetProgramExecutableName(); + char *args[] = {prog, NULL}; + char *envs[] = {"THE_DOGE=42", NULL}; + newsa.sa_flags = SA_SIGINFO; + newsa.sa_sigaction = OnSigchld; + sigemptyset(&newsa.sa_mask); + ASSERT_SYS(0, 0, sigaction(SIGCHLD, &newsa, &oldsa)); + sigemptyset(&blocksigchld); + sigaddset(&blocksigchld, SIGCHLD); + ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &blocksigchld, &oldmask)); + EXPECT_EQ(0, posix_spawn(&sigchld_pid, prog, NULL, NULL, args, envs)); + sigemptyset(&unblockall); + EXPECT_SYS(EINTR, -1, sigsuspend(&unblockall)); + EXPECT_TRUE(sigchld_got_signal); + EXPECT_SYS(0, 0, sigaction(SIGCHLD, &oldsa, 0)); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldmask, 0)); +} + //////////////////////////////////////////////////////////////////////////////// void ForkExecveWait(const char *prog) { @@ -299,6 +385,7 @@ BENCH(posix_spawn, bench) { close(3); testlib_extract("/zip/life.com", "life.com", 0755); testlib_extract("/zip/life.elf", "life.elf", 0755); + testlib_extract("/zip/life-pe.com", "life-pe.com", 0755); kprintf("%s %s (MODE=" MODE " rss=%'zu tiny64=%'zu life.com=%'zu life.elf=%'zu)\n", __describe_os(), GetHost(), GetRss(), GetSize("tiny64"), @@ -307,6 +394,12 @@ BENCH(posix_spawn, bench) { EZBENCH2("posix_spawn life.com", donothing, PosixSpawnWait("./life.com")); EZBENCH2("vfork life.com", donothing, VforkExecveWait("./life.com")); EZBENCH2("fork life.com", donothing, ForkExecveWait("./life.com")); + if (IsWindows()) { + EZBENCH2("posix_spawn life-pe.com", donothing, + PosixSpawnWait("./life-pe.com")); + EZBENCH2("vfork life-pe.com", donothing, VforkExecveWait("./life-pe.com")); + EZBENCH2("fork life-pe.com", donothing, ForkExecveWait("./life-pe.com")); + } if (IsXnu() || IsWindows() || IsMetal()) return; EZBENCH2("posix_spawn life.elf", donothing, PosixSpawnWait("./life.elf")); EZBENCH2("vfork life.elf", donothing, VforkExecveWait("./life.elf")); diff --git a/test/libc/stdio/system_test.c b/test/libc/proc/system_test.c similarity index 61% rename from test/libc/stdio/system_test.c rename to test/libc/proc/system_test.c index 9c0b846b2..a7cdf6cc2 100644 --- a/test/libc/stdio/system_test.c +++ b/test/libc/proc/system_test.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/cosmo.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.h" #include "libc/mem/gc.h" #include "libc/mem/gc.internal.h" #include "libc/paths.h" @@ -32,23 +33,27 @@ #include "libc/x/x.h" #ifdef __x86_64__ +#define GETEXITSTATUS(x) \ + ({ \ + int status_ = (x); \ + if (WIFSIGNALED(status_)) { \ + kprintf("%s:%d: %s terminated with %G\n", __FILE__, __LINE__, #x, \ + WTERMSIG(status_)); \ + exit(1); \ + } \ + WEXITSTATUS(status_); \ + }) + __static_yoink("_tr"); __static_yoink("glob"); -char testlib_enable_tmp_setup_teardown; - -void SetUp(void) { - if (IsWindows()) { - fprintf(stderr, - "TODO(jart): Why does system_test have issues on Windows when " - "running as a subprocess of something like runitd.com?\n"); - exit(0); - } -} - int pipefd[2]; int stdoutBack; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + void CaptureStdout(void) { ASSERT_NE(-1, (stdoutBack = dup(1))); ASSERT_SYS(0, 0, pipe(pipefd)); @@ -72,7 +77,7 @@ TEST(system, echo) { } TEST(system, exit) { - ASSERT_EQ(123, WEXITSTATUS(system("exit 123"))); + ASSERT_EQ(123, GETEXITSTATUS(system("exit 123"))); } TEST(system, testStdoutRedirect) { @@ -94,7 +99,7 @@ TEST(system, testStderrRedirect_toStdout) { char buf[5] = {0}; ASSERT_NE(-1, dup2(1, 2)); bool success = false; - if (WEXITSTATUS(system("echo aaa 2>&1")) == 0) { + if (GETEXITSTATUS(system("echo aaa 2>&1")) == 0) { success = read(pipefd[0], buf, 4) == (4); } ASSERT_NE(-1, dup2(stderrBack, 2)); @@ -107,7 +112,7 @@ TEST(system, testStderrRedirect_toStdout) { testlib_extract("/zip/echo.com", "echo.com", 0755); ASSERT_NE(-1, dup2(1, 2)); success = false; - if (WEXITSTATUS(system("./echo.com aaa 2>&1")) == 0) { + if (GETEXITSTATUS(system("./echo.com aaa 2>&1")) == 0) { success = read(pipefd[0], buf, 4) == (4); } ASSERT_NE(-1, dup2(stderrBack, 2)); @@ -117,30 +122,30 @@ TEST(system, testStderrRedirect_toStdout) { } TEST(system, and) { - ASSERT_EQ(1, WEXITSTATUS(system("false && false"))); - ASSERT_EQ(1, WEXITSTATUS(system("true&& false"))); - ASSERT_EQ(1, WEXITSTATUS(system("false &&true"))); - ASSERT_EQ(0, WEXITSTATUS(system("true&&true"))); + ASSERT_EQ(1, GETEXITSTATUS(system("false && false"))); + ASSERT_EQ(1, GETEXITSTATUS(system("true&& false"))); + ASSERT_EQ(1, GETEXITSTATUS(system("false &&true"))); + ASSERT_EQ(0, GETEXITSTATUS(system("true&&true"))); } TEST(system, or) { - ASSERT_EQ(1, WEXITSTATUS(system("false || false"))); - ASSERT_EQ(0, WEXITSTATUS(system("true|| false"))); - ASSERT_EQ(0, WEXITSTATUS(system("false ||true"))); - ASSERT_EQ(0, WEXITSTATUS(system("true||true"))); + ASSERT_EQ(1, GETEXITSTATUS(system("false || false"))); + ASSERT_EQ(0, GETEXITSTATUS(system("true|| false"))); + ASSERT_EQ(0, GETEXITSTATUS(system("false ||true"))); + ASSERT_EQ(0, GETEXITSTATUS(system("true||true"))); } TEST(system, async1) { - ASSERT_EQ(123, WEXITSTATUS(system("exit 123 & " - "wait $!"))); + ASSERT_EQ(123, GETEXITSTATUS(system("exit 123 & " + "wait $!"))); } TEST(system, async4) { - ASSERT_EQ(0, WEXITSTATUS(system("echo a >a & " - "echo b >b & " - "echo c >c & " - "echo d >d & " - "wait"))); + ASSERT_EQ(0, GETEXITSTATUS(system("echo a >a & " + "echo b >b & " + "echo c >c & " + "echo d >d & " + "wait"))); ASSERT_TRUE(fileexists("a")); ASSERT_TRUE(fileexists("b")); ASSERT_TRUE(fileexists("c")); @@ -149,20 +154,20 @@ TEST(system, async4) { TEST(system, equals) { setenv("var", "wand", true); - ASSERT_EQ(0, WEXITSTATUS(system("test a = a"))); - ASSERT_EQ(1, WEXITSTATUS(system("test a = b"))); - ASSERT_EQ(0, WEXITSTATUS(system("test x$var = xwand"))); - ASSERT_EQ(0, WEXITSTATUS(system("[ a = a ]"))); - ASSERT_EQ(1, WEXITSTATUS(system("[ a = b ]"))); + ASSERT_EQ(0, GETEXITSTATUS(system("test a = a"))); + ASSERT_EQ(1, GETEXITSTATUS(system("test a = b"))); + ASSERT_EQ(0, GETEXITSTATUS(system("test x$var = xwand"))); + ASSERT_EQ(0, GETEXITSTATUS(system("[ a = a ]"))); + ASSERT_EQ(1, GETEXITSTATUS(system("[ a = b ]"))); } TEST(system, notequals) { - ASSERT_EQ(1, WEXITSTATUS(system("test a != a"))); - ASSERT_EQ(0, WEXITSTATUS(system("test a != b"))); + ASSERT_EQ(1, GETEXITSTATUS(system("test a != a"))); + ASSERT_EQ(0, GETEXITSTATUS(system("test a != b"))); } TEST(system, usleep) { - ASSERT_EQ(0, WEXITSTATUS(system("usleep & kill $!"))); + ASSERT_EQ(0, GETEXITSTATUS(system("usleep & kill $!"))); } TEST(system, kill) { @@ -172,19 +177,19 @@ TEST(system, kill) { TEST(system, exitStatusPreservedAfterSemiColon) { testlib_extract("/zip/false.com", "false.com", 0755); - ASSERT_EQ(1, WEXITSTATUS(system("false;"))); - ASSERT_EQ(1, WEXITSTATUS(system("false; "))); - ASSERT_EQ(1, WEXITSTATUS(system("./false.com;"))); - ASSERT_EQ(1, WEXITSTATUS(system("./false.com;"))); + ASSERT_EQ(1, GETEXITSTATUS(system("false;"))); + ASSERT_EQ(1, GETEXITSTATUS(system("false; "))); + ASSERT_EQ(1, GETEXITSTATUS(system("./false.com;"))); + ASSERT_EQ(1, GETEXITSTATUS(system("./false.com;"))); CaptureStdout(); - ASSERT_EQ(0, WEXITSTATUS(system("false; echo $?"))); + ASSERT_EQ(0, GETEXITSTATUS(system("false; echo $?"))); char buf[9] = {0}; ASSERT_EQ(2, read(pipefd[0], buf, 8)); ASSERT_STREQ("1\n", buf); - ASSERT_EQ(0, WEXITSTATUS(system("./false.com; echo $?"))); + ASSERT_EQ(0, GETEXITSTATUS(system("./false.com; echo $?"))); ASSERT_EQ(2, read(pipefd[0], buf, 8)); ASSERT_STREQ("1\n", buf); - ASSERT_EQ(0, WEXITSTATUS(system("echo -n hi"))); + ASSERT_EQ(0, GETEXITSTATUS(system("echo -n hi"))); EXPECT_EQ(2, read(pipefd[0], buf, 8)); ASSERT_STREQ("hi", buf); RestoreStdout(); @@ -195,7 +200,7 @@ TEST(system, globio) { CaptureStdout(); ASSERT_SYS(0, 0, touch("a", 0644)); ASSERT_SYS(0, 0, touch("b", 0644)); - ASSERT_EQ(0, WEXITSTATUS(system("echo *"))); + ASSERT_EQ(0, GETEXITSTATUS(system("echo *"))); EXPECT_EQ(4, read(pipefd[0], buf, 8)); ASSERT_STREQ("a b\n", buf); RestoreStdout(); @@ -204,12 +209,12 @@ TEST(system, globio) { TEST(system, allowsLoneCloseCurlyBrace) { CaptureStdout(); char buf[6] = {0}; - ASSERT_EQ(0, WEXITSTATUS(system("echo \"aaa\"}"))); + ASSERT_EQ(0, GETEXITSTATUS(system("echo \"aaa\"}"))); ASSERT_EQ(5, read(pipefd[0], buf, 5)); ASSERT_STREQ("aaa}\n", buf); bzero(buf, 6); testlib_extract("/zip/echo.com", "echo.com", 0755); - ASSERT_EQ(0, WEXITSTATUS(system("./echo.com \"aaa\"}"))); + ASSERT_EQ(0, GETEXITSTATUS(system("./echo.com \"aaa\"}"))); ASSERT_EQ(5, read(pipefd[0], buf, 5)); ASSERT_STREQ("aaa}\n", buf); RestoreStdout(); @@ -222,23 +227,45 @@ TEST(system, glob) { } TEST(system, env) { - ASSERT_EQ(0, system("env - a=b c=d >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env - a=b c=d >res"))); ASSERT_STREQ("a=b\nc=d\n", gc(xslurp("res", 0))); - ASSERT_EQ(0, system("env -i -0 a=b c=d >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env -i -0 a=b c=d >res"))); ASSERT_STREQN("a=b\0c=d\0", gc(xslurp("res", 0)), 8); - ASSERT_EQ(0, system("env -i0 a=b c=d >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env -i0 a=b c=d >res"))); ASSERT_STREQN("a=b\0c=d\0", gc(xslurp("res", 0)), 8); - ASSERT_EQ(0, system("env - a=b c=d -u a z=g >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env - a=b c=d -u a z=g >res"))); ASSERT_STREQ("c=d\nz=g\n", gc(xslurp("res", 0))); - ASSERT_EQ(0, system("env - a=b c=d -ua z=g >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env - a=b c=d -ua z=g >res"))); ASSERT_STREQ("c=d\nz=g\n", gc(xslurp("res", 0))); - ASSERT_EQ(0, system("env - dope='show' >res")); + ASSERT_EQ(0, GETEXITSTATUS(system("env - dope='show' >res"))); ASSERT_STREQ("dope=show\n", gc(xslurp("res", 0))); } -TEST(system, tr) { - ASSERT_EQ(0, system("echo hello | tr a-z A-Z >res")); +TEST(system, pipelineCanOutputToFile) { + ASSERT_EQ(0, GETEXITSTATUS(system("echo hello | tr a-z A-Z >res"))); ASSERT_STREQ("HELLO\n", gc(xslurp("res", 0))); + testlib_extract("/zip/echo.com", "echo.com", 0755); + ASSERT_EQ(0, GETEXITSTATUS(system("./echo.com hello | tr a-z A-Z >res"))); + ASSERT_STREQ("HELLO\n", gc(xslurp("res", 0))); +} + +TEST(system, pipelineCanOutputBackToSelf) { + char buf[16] = {0}; + CaptureStdout(); + ASSERT_EQ(0, GETEXITSTATUS(system("echo hello | tr a-z A-Z"))); + ASSERT_SYS(0, 6, read(pipefd[0], buf, 16)); + ASSERT_STREQ("HELLO\n", buf); + ASSERT_EQ(0, GETEXITSTATUS(system("echo hello | exec tr a-z A-Z"))); + ASSERT_SYS(0, 6, read(pipefd[0], buf, 16)); + ASSERT_STREQ("HELLO\n", buf); + testlib_extract("/zip/echo.com", "echo.com", 0755); + ASSERT_EQ(0, GETEXITSTATUS(system("./echo.com hello | tr a-z A-Z"))); + ASSERT_SYS(0, 6, read(pipefd[0], buf, 16)); + ASSERT_STREQ("HELLO\n", buf); + ASSERT_EQ(0, GETEXITSTATUS(system("./echo.com hello | exec tr a-z A-Z"))); + ASSERT_SYS(0, 6, read(pipefd[0], buf, 16)); + ASSERT_STREQ("HELLO\n", buf); + RestoreStdout(); } int system2(const char *); diff --git a/test/libc/proc/test.mk b/test/libc/proc/test.mk new file mode 100644 index 000000000..b41372d4a --- /dev/null +++ b/test/libc/proc/test.mk @@ -0,0 +1,95 @@ +#-*-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 += TEST_LIBC_PROC + +TEST_LIBC_PROC_SRCS := $(wildcard test/libc/proc/*.c) +TEST_LIBC_PROC_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_PROC_SRCS)) + +TEST_LIBC_PROC_OBJS = \ + $(TEST_LIBC_PROC_SRCS:%.c=o/$(MODE)/%.o) + +TEST_LIBC_PROC_COMS = \ + $(TEST_LIBC_PROC_SRCS:%.c=o/$(MODE)/%.com) + +TEST_LIBC_PROC_BINS = \ + $(TEST_LIBC_PROC_COMS) \ + $(TEST_LIBC_PROC_COMS:%=%.dbg) + +TEST_LIBC_PROC_TESTS = \ + $(TEST_LIBC_PROC_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + +TEST_LIBC_PROC_CHECKS = \ + $(TEST_LIBC_PROC_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + +TEST_LIBC_PROC_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_PROC \ + LIBC_STR \ + LIBC_SYSV \ + LIBC_TESTLIB \ + LIBC_THREAD \ + LIBC_X \ + THIRD_PARTY_MUSL \ + THIRD_PARTY_TR + +TEST_LIBC_PROC_DEPS := \ + $(call uniq,$(foreach x,$(TEST_LIBC_PROC_DIRECTDEPS),$($(x)))) + +o/$(MODE)/test/libc/proc/proc.pkg: \ + $(TEST_LIBC_PROC_OBJS) \ + $(foreach x,$(TEST_LIBC_PROC_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/test/libc/proc/%.com.dbg: \ + $(TEST_LIBC_PROC_DEPS) \ + o/$(MODE)/test/libc/proc/%.o \ + o/$(MODE)/test/libc/proc/proc.pkg \ + o/$(MODE)/tool/build/echo.com.zip.o \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/proc/posix_spawn_test.com.runs: \ + private QUOTA += -M8192m + +o/$(MODE)/test/libc/proc/posix_spawn_test.com.dbg: \ + $(TEST_LIBC_PROC_DEPS) \ + o/$(MODE)/test/libc/proc/posix_spawn_test.o \ + o/$(MODE)/test/libc/proc/proc.pkg \ + o/$(MODE)/tool/build/echo.com.zip.o \ + o/$(MODE)/test/libc/mem/prog/life.com.zip.o \ + o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ + o/$(MODE)/test/libc/proc/life-pe.com.zip.o \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/proc/system_test.com.dbg: \ + $(TEST_LIBC_PROC_DEPS) \ + o/$(MODE)/test/libc/proc/system_test.o \ + o/$(MODE)/test/libc/proc/proc.pkg \ + o/$(MODE)/tool/build/echo.com.zip.o \ + o/$(MODE)/tool/build/cocmd.com.zip.o \ + o/$(MODE)/tool/build/false.com.zip.o \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/proc/life-pe.com.zip.o: private \ + ZIPOBJ_FLAGS += \ + -B + +$(TEST_LIBC_PROC_OBJS): test/libc/proc/test.mk + +.PHONY: o/$(MODE)/test/libc/proc +o/$(MODE)/test/libc/proc: \ + $(TEST_LIBC_PROC_BINS) \ + $(TEST_LIBC_PROC_CHECKS) diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c deleted file mode 100644 index ecb037b42..000000000 --- a/test/libc/runtime/clone_test.c +++ /dev/null @@ -1,138 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/errno.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/stack.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" - -int x, me, tid; -atomic_int thechilde; - -void SetUpOnce(void) { - __enable_threads(); - ASSERT_SYS(0, 0, pledge("stdio rpath", 0)); -} - -int Hog(void *arg, int tid) { - return 0; -} - -void SetUp(void) { - x = 0; - me = gettid(); -} - -void TearDown(void) { -} - -int DoNothing(void *arg) { - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// TEST THREADS WORK - -int CloneTest1(void *arg, int tid) { - intptr_t rsp, top, bot; - CheckStackIsAligned(); - // PrintBacktraceUsingSymbols(2, __builtin_frame_address(0), - // GetSymbolTable()); - rsp = (intptr_t)__builtin_frame_address(0); - bot = ROUNDDOWN((intptr_t)rsp, GetStackSize()); - top = bot + GetStackSize(); - ASSERT_GT(rsp, bot); // check we're on stack - ASSERT_LT(rsp, top); // check we're on stack - ASSERT_GT(rsp, top - 256); // check we're near top of stack - ASSERT_TRUE(IS2POW(GetStackSize())); - ASSERT_EQ(0, bot & (GetStackSize() - 1)); - x = 42; - ASSERT_EQ(23, (intptr_t)arg); - ASSERT_NE(gettid(), getpid()); - return 0; -} - -TEST(clone, test1) { - int ptid = 0; - struct spawn th; - ASSERT_SYS(0, 0, _spawn(CloneTest1, (void *)23, &th)); - ASSERT_SYS(0, 0, _join(&th)); - ASSERT_NE(gettid(), tid); - ASSERT_EQ(tid, ptid); - ASSERT_EQ(42, x); - ASSERT_NE(me, tid); - ASSERT_EQ(0, errno); - errno = 31337; - ASSERT_EQ(31337, errno); - errno = 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// TEST THREADS CAN ISSUE SYSTEM CALLS WITH INDEPENDENT ERRNOS - -atomic_int sysbarrier; - -int CloneTestSys(void *arg, int tid) { - int i, id = (intptr_t)arg; - CheckStackIsAligned(); - while (!sysbarrier) donothing; - for (i = 0; i < 20; ++i) { - switch (id % 3) { - case 0: - errno = 123; - open(0, 0); - donothing; - ASSERT_EQ(EFAULT, errno); - break; - case 1: - errno = 123; - dup(-1); - donothing; - ASSERT_EQ(EBADF, errno); - break; - case 2: - errno = 123; - dup3(0, 0, 0); - donothing; - ASSERT_EQ(EINVAL, errno); - break; - default: - __builtin_unreachable(); - } - } - return 0; -} - -TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { - int i; - struct spawn th[8]; - ASSERT_EQ(0, errno); - for (i = 0; i < 8; ++i) { - ASSERT_SYS(0, 0, _spawn(CloneTestSys, (void *)(intptr_t)i, th + i)); - } - sysbarrier = 1; - for (i = 0; i < 8; ++i) { - ASSERT_SYS(0, 0, _join(th + i)); - } - ASSERT_EQ(0, errno); -} diff --git a/test/libc/runtime/daemon_test.c b/test/libc/runtime/daemon_test.c index 9300ecb40..6bf2954c8 100644 --- a/test/libc/runtime/daemon_test.c +++ b/test/libc/runtime/daemon_test.c @@ -17,26 +17,24 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/struct/sigset.h" #include "libc/dce.h" -#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -#include "libc/time/time.h" -#include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(daemon, test) { - char buf[512]; + char buf[512] = {0}; SPAWN(fork); ASSERT_SYS(0, 3, open(".", O_RDONLY | O_DIRECTORY)); ASSERT_SYS(0, 0, daemon(false, false)); ASSERT_SYS(0, 4, openat(3, "ok", O_WRONLY | O_CREAT | O_TRUNC, 0644)); ASSERT_NE(NULL, getcwd(buf, sizeof(buf))); - ASSERT_SYS(0, 1, write(4, buf, strlen(buf))); + ASSERT_SYS(0, IsWindows() ? 3 : 1, write(4, buf, strlen(buf))); ASSERT_SYS(0, 0, close(4)); ASSERT_SYS(0, 0, close(3)); EXITS(0); @@ -50,5 +48,4 @@ TEST(daemon, test) { } usleep(1000L << i); } - ASSERT_TRUE(false); } diff --git a/test/libc/runtime/ftrace_test.c b/test/libc/runtime/ftrace_test.c index 08bb698e2..fadc45067 100644 --- a/test/libc/runtime/ftrace_test.c +++ b/test/libc/runtime/ftrace_test.c @@ -24,7 +24,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(ftrace, test) { if (1) { diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index 4c798b330..fb98edef9 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -24,14 +24,13 @@ #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { if (!IsWindows()) { // TODO(jart): mock out that win32 i/o call tinyprint(2, program_invocation_name, ": skipping on non-windows\n", NULL); exit(0); } + testlib_enable_tmp_setup_teardown(); } TEST(GetDosArgv, empty) { diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 66ca294b4..a3278aee3 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -52,9 +52,8 @@ __static_yoink("zipos"); -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); // ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc", 0)); } @@ -242,14 +241,14 @@ TEST(isheap, malloc) { ASSERT_TRUE(_isheap(_gc(malloc(1)))); } -TEST(isheap, emptyMalloc) { - ASSERT_TRUE(_isheap(_gc(malloc(0)))); -} +/* TEST(isheap, emptyMalloc) { */ +/* ASSERT_TRUE(_isheap(_gc(malloc(0)))); */ +/* } */ -TEST(isheap, mallocOffset) { - char *p = _gc(malloc(131072)); - ASSERT_TRUE(_isheap(p + 100000)); -} +/* TEST(isheap, mallocOffset) { */ +/* char *p = _gc(malloc(131072)); */ +/* ASSERT_TRUE(_isheap(p + 100000)); */ +/* } */ static const char *ziposLifePath = "/zip/life.elf"; TEST(mmap, ziposCannotBeAnonymous) { diff --git a/test/libc/runtime/mprotect_test.c b/test/libc/runtime/mprotect_test.c index 4b8dd12d0..6badca95f 100644 --- a/test/libc/runtime/mprotect_test.c +++ b/test/libc/runtime/mprotect_test.c @@ -42,7 +42,10 @@ volatile bool gotsegv; volatile bool gotbusted; struct sigaction old[2]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} #ifdef __x86_64__ static const char kRet31337[] = { diff --git a/test/libc/runtime/munmap_test.c b/test/libc/runtime/munmap_test.c index b29bf5d88..0b50b687e 100644 --- a/test/libc/runtime/munmap_test.c +++ b/test/libc/runtime/munmap_test.c @@ -26,7 +26,9 @@ #include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(munmap, doesntExist_doesntCare) { EXPECT_SYS(0, 0, munmap(0, FRAMESIZE * 8)); diff --git a/test/libc/runtime/test.mk b/test/libc/runtime/test.mk index b2ade544c..605686ad7 100644 --- a/test/libc/runtime/test.mk +++ b/test/libc/runtime/test.mk @@ -29,6 +29,7 @@ TEST_LIBC_RUNTIME_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/test/libc/runtime/zipos_test.c b/test/libc/runtime/zipos_test.c index 9b2872094..3db1139b9 100644 --- a/test/libc/runtime/zipos_test.c +++ b/test/libc/runtime/zipos_test.c @@ -21,6 +21,7 @@ #include "libc/errno.h" #include "libc/limits.h" #include "libc/mem/gc.h" +#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/runtime/zipos.internal.h" @@ -28,7 +29,7 @@ #include "libc/sysv/consts/o.h" #include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" __static_yoink("zipos"); __static_yoink("libc/testlib/hyperion.txt"); @@ -36,7 +37,7 @@ __static_yoink("_Cz_inflate"); __static_yoink("_Cz_inflateInit2"); __static_yoink("_Cz_inflateEnd"); -int Worker(void *arg, int tid) { +void *Worker(void *arg) { int i, fd; char *data; for (i = 0; i < 20; ++i) { @@ -52,9 +53,13 @@ int Worker(void *arg, int tid) { TEST(zipos, test) { int i, n = 16; - struct spawn *t = _gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); + for (i = 0; i < n; ++i) { + ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0)); + } + for (i = 0; i < n; ++i) { + EXPECT_SYS(0, 0, pthread_join(t[i], 0)); + } __print_maps(); } diff --git a/test/libc/sock/nointernet_test.c b/test/libc/sock/nointernet_test.c deleted file mode 100644 index ffc9a8a06..000000000 --- a/test/libc/sock/nointernet_test.c +++ /dev/null @@ -1,89 +0,0 @@ -/*-*- 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/struct/stat.h" -#include "libc/calls/syscall_support-sysv.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/log/log.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/sock.h" -#include "libc/sock/struct/msghdr.h" -#include "libc/sock/struct/sockaddr.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/sock.h" -#include "libc/testlib/testlib.h" -#ifdef __x86_64__ - -char testlib_enable_tmp_setup_teardown; - -void SetUpOnce(void) { - if (nointernet() == -1) { - ASSERT_TRUE(errno == EPERM || // already traced - errno == ENOSYS); // non-linux or ancient linux - if (errno == ENOSYS) { - exit(0); - } - } -} - -TEST(nointernet, testLocalhost_isAllowed) { - struct sockaddr_in addr = {AF_INET, 0, {htonl(0x7f000001)}}; - ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); - ASSERT_SYS(ECONNREFUSED, -1, - connect(3, (struct sockaddr *)&addr, sizeof(addr))); - ASSERT_SYS(0, 0, close(3)); -} - -TEST(nointernet, testPublicInternet_isDenied) { - struct sockaddr_in addr = {AF_INET, 0, {htonl(0x06060600)}}; - ASSERT_SYS(EPERM, -1, socket(AF_BLUETOOTH, SOCK_STREAM, IPPROTO_TCP)); - ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - ASSERT_SYS(ENOSYS, -1, bind(3, (struct sockaddr *)&addr, sizeof(addr))); - ASSERT_SYS(ENOSYS, -1, connect(3, (struct sockaddr *)&addr, sizeof(addr))); - ASSERT_SYS(0, 0, close(3)); -} - -TEST(nointernet, sendmsgPrivateNetwork_doesntGetBlocked) { - struct msghdr msg = { - .msg_name = &(struct sockaddr_in){AF_INET, 31337, {htonl(0x0a000001)}}, - .msg_namelen = sizeof(struct sockaddr_in), - .msg_iov = &(struct iovec){"hi", 2}, - .msg_iovlen = 1, - }; - ASSERT_SYS(EBADF, -1, sendmsg(-1, &msg, 0)); -} - -TEST(nointernet, sendmsgPublicNetwork_raisesEnosys_whichPreemptsEbadf) { - struct msghdr msg = { - .msg_name = &(struct sockaddr_in){AF_INET, 31337, {htonl(0x06060600)}}, - .msg_namelen = sizeof(struct sockaddr_in), - .msg_iov = &(struct iovec){"hi", 2}, - .msg_iovlen = 1, - }; - ASSERT_SYS(ENOSYS, -1, sendmsg(-1, &msg, 0)); - ASSERT_SYS(0, 3, socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); - ASSERT_SYS(ENOSYS, -1, sendmsg(3, &msg, 0)); - ASSERT_SYS(0, 0, close(3)); -} - -#endif /* __x86_64__ */ diff --git a/test/libc/sock/sendfile_test.c b/test/libc/sock/sendfile_test.c index d6316ceef..4c380e572 100644 --- a/test/libc/sock/sendfile_test.c +++ b/test/libc/sock/sendfile_test.c @@ -38,12 +38,10 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { - __enable_threads(); if (IsNetbsd()) exit(0); if (IsOpenbsd()) exit(0); + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc inet", 0)); } diff --git a/test/libc/sock/test.mk b/test/libc/sock/test.mk index b2ecdda48..aa3d3f7c5 100644 --- a/test/libc/sock/test.mk +++ b/test/libc/sock/test.mk @@ -28,6 +28,7 @@ TEST_LIBC_SOCK_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ @@ -69,8 +70,7 @@ o/$(MODE)/test/libc/sock/poll_test.com.runs \ o/$(MODE)/test/libc/sock/pollfd_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc inet -o/$(MODE)/test/libc/sock/sendrecvmsg_test.com.runs \ -o/$(MODE)/test/libc/sock/nointernet_test.com.runs: \ +o/$(MODE)/test/libc/sock/sendrecvmsg_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc inet recvfd sendfd o/$(MODE)/test/libc/sock/socket_test.com.runs: \ diff --git a/test/libc/sock/unix_test.c b/test/libc/sock/unix_test.c index f96d1906d..1f341a607 100644 --- a/test/libc/sock/unix_test.c +++ b/test/libc/sock/unix_test.c @@ -37,9 +37,8 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath cpath proc unix", 0)); } diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index 24ec09821..50e14e51b 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -48,7 +48,9 @@ __static_yoink("usr/share/zoneinfo/New_York"); __static_yoink("libc/testlib/hyperion.txt"); __static_yoink("libc/testlib/moby.txt"); -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} DIR *dir; struct dirent *ent; diff --git a/test/libc/stdio/dtoa_test.c b/test/libc/stdio/dtoa_test.c index cc491c9c6..fb42dbfb5 100644 --- a/test/libc/stdio/dtoa_test.c +++ b/test/libc/stdio/dtoa_test.c @@ -34,8 +34,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sched.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" -#include "libc/thread/wait0.internal.h" +#include "libc/thread/thread.h" #include "libc/x/x.h" #define DUB(i) (union Dub){i}.x @@ -53,11 +52,10 @@ union Dub { }; void SetUpOnce(void) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio", 0)); } -int Worker(void *p, int tid) { +void *Worker(void *p) { int i; char str[64]; for (i = 0; i < 256; ++i) { @@ -70,9 +68,13 @@ int Worker(void *p, int tid) { TEST(dtoa, locks) { int i, n = 32; - struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); + for (i = 0; i < n; ++i) { + ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0)); + } + for (i = 0; i < n; ++i) { + EXPECT_EQ(0, pthread_join(t[i], 0)); + } } static const struct { diff --git a/test/libc/stdio/fmt_test.c b/test/libc/stdio/fmt_test.c index 2077056df..7382b877e 100644 --- a/test/libc/stdio/fmt_test.c +++ b/test/libc/stdio/fmt_test.c @@ -74,6 +74,24 @@ TEST(fmt, u) { EXPECT_STREQ("042 ", _gc(xasprintf("%-4.3u", 42))); } +TEST(fmt, c) { + char buf[8]; + EXPECT_EQ(1, sprintf(buf, "%c", 'h')); + EXPECT_STREQ("h", buf); + EXPECT_EQ(3, sprintf(buf, "%`c", 'h')); + EXPECT_STREQ("'h'", buf); + EXPECT_EQ(4, sprintf(buf, "%`c", '\t')); + EXPECT_STREQ("'\\t'", buf); + EXPECT_EQ(4, sprintf(buf, "%`c", 0)); + EXPECT_STREQ("'\\0'", buf); + EXPECT_EQ(3, sprintf(buf, "%#c", 1)); + EXPECT_STREQ("☺", buf); + EXPECT_EQ(2, sprintf(buf, "%#c", 0)); + EXPECT_STREQ(" ", buf); + EXPECT_EQ(4, sprintf(buf, "%#`c", 0)); + EXPECT_STREQ("'\\0'", buf); +} + TEST(fmt, x) { EXPECT_STREQ("0x01 ", _gc(xasprintf("%#-07.2x", 1))); EXPECT_STREQ("0x00136d ", _gc(xasprintf("%#-010.6x", 4973))); diff --git a/test/libc/stdio/fputc_test.c b/test/libc/stdio/fputc_test.c index 53d013212..4ea30e81d 100644 --- a/test/libc/stdio/fputc_test.c +++ b/test/libc/stdio/fputc_test.c @@ -25,7 +25,10 @@ FILE *f; char buf[512]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fputc, test) { ASSERT_NE(NULL, (f = fopen("hog", "w+"))); @@ -65,7 +68,6 @@ TEST(fgetc, testUnbuffered) { } BENCH(fputc, bench) { - __enable_threads(); FILE *f; ASSERT_NE(NULL, (f = fopen("/dev/null", "w"))); EZBENCH2("fputc", donothing, fputc('E', f)); diff --git a/test/libc/stdio/fputs_test.c b/test/libc/stdio/fputs_test.c index 71e28c9a3..555b385b6 100644 --- a/test/libc/stdio/fputs_test.c +++ b/test/libc/stdio/fputs_test.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" #include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" @@ -26,7 +26,10 @@ FILE *f; char buf[512]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fputs, test) { ASSERT_NE(NULL, (f = fopen("hog", "w"))); diff --git a/test/libc/stdio/fread_test.c b/test/libc/stdio/fread_test.c index c15372b21..b1762ca37 100644 --- a/test/libc/stdio/fread_test.c +++ b/test/libc/stdio/fread_test.c @@ -20,7 +20,9 @@ #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fread, eofIsSticky) { FILE *fo, *fi; diff --git a/test/libc/stdio/freopen_test.c b/test/libc/stdio/freopen_test.c index 6232280d8..9d9a08b80 100644 --- a/test/libc/stdio/freopen_test.c +++ b/test/libc/stdio/freopen_test.c @@ -24,7 +24,9 @@ * https://github.com/jart/cosmopolitan/issues/61#issuecomment-792214575 */ -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} int writefile(const char* filename) { int stat = 0; diff --git a/test/libc/stdio/fseeko_test.c b/test/libc/stdio/fseeko_test.c index af2dad344..9b5064cd2 100644 --- a/test/libc/stdio/fseeko_test.c +++ b/test/libc/stdio/fseeko_test.c @@ -21,7 +21,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fseeko, test) { FILE *f; diff --git a/test/libc/stdio/ftell_test.c b/test/libc/stdio/ftell_test.c index 7931c628a..994e4a127 100644 --- a/test/libc/stdio/ftell_test.c +++ b/test/libc/stdio/ftell_test.c @@ -25,7 +25,10 @@ FILE *f; char buf[512]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(ftell, test) { ASSERT_NE(NULL, (f = fopen("hog", "w"))); diff --git a/test/libc/stdio/fwrite_test.c b/test/libc/stdio/fwrite_test.c index c9a232a39..11d5424ce 100644 --- a/test/libc/stdio/fwrite_test.c +++ b/test/libc/stdio/fwrite_test.c @@ -34,7 +34,10 @@ FILE *f; char buf[512]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(fwrite, test) { ASSERT_NE(NULL, (f = fopen(PATH, "wb"))); diff --git a/test/libc/stdio/getdelim_test.c b/test/libc/stdio/getdelim_test.c index c6461a193..4a6df24b5 100644 --- a/test/libc/stdio/getdelim_test.c +++ b/test/libc/stdio/getdelim_test.c @@ -28,7 +28,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(getline, testEmpty) { FILE *f = fmemopen("", 0, "r+"); diff --git a/test/libc/stdio/gz_test.c b/test/libc/stdio/gz_test.c index 395858021..4de9c0056 100644 --- a/test/libc/stdio/gz_test.c +++ b/test/libc/stdio/gz_test.c @@ -22,7 +22,9 @@ #include "libc/testlib/testlib.h" #include "third_party/zlib/zlib.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(gz, test) { int fd; diff --git a/test/libc/stdio/memory_test.c b/test/libc/stdio/memory_test.c index fee346f42..b5eefd2d9 100644 --- a/test/libc/stdio/memory_test.c +++ b/test/libc/stdio/memory_test.c @@ -17,14 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/mem/mem.h" #include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" #include "libc/runtime/internal.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" +#include "libc/thread/thread.h" -int Worker(void *arg, int tid) { +void *Worker(void *arg) { int i; char *volatile p; char *volatile q; @@ -41,13 +41,16 @@ int Worker(void *arg, int tid) { } void SetUpOnce(void) { - __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio", 0)); } TEST(memory, test) { int i, n = 32; - struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); + for (i = 0; i < n; ++i) { + ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0)); + } + for (i = 0; i < n; ++i) { + EXPECT_EQ(0, pthread_join(t[i], 0)); + } } diff --git a/test/libc/stdio/popen_test.c b/test/libc/stdio/popen_test.c index aeafd2f7c..d54ee6f3f 100644 --- a/test/libc/stdio/popen_test.c +++ b/test/libc/stdio/popen_test.c @@ -39,7 +39,10 @@ FILE *f; char buf[32]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void CheckForFdLeaks(void) { int rc, i, l = 0, e = errno; diff --git a/test/libc/stdio/sscanf_test.c b/test/libc/stdio/sscanf_test.c index 5a9d92d55..e1408c09a 100644 --- a/test/libc/stdio/sscanf_test.c +++ b/test/libc/stdio/sscanf_test.c @@ -21,8 +21,8 @@ #include "libc/intrin/bits.h" #include "libc/inttypes.h" #include "libc/limits.h" -#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index 2fe0b5f38..7d83a8de1 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -28,6 +28,7 @@ TEST_LIBC_STDIO_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ @@ -41,7 +42,6 @@ TEST_LIBC_STDIO_DIRECTDEPS = \ THIRD_PARTY_GDTOA \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_MUSL \ - THIRD_PARTY_TR \ THIRD_PARTY_NSYNC \ THIRD_PARTY_ZLIB \ THIRD_PARTY_ZLIB_GZ @@ -63,18 +63,6 @@ o/$(MODE)/test/libc/stdio/%.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/test/libc/stdio/system_test.com.dbg: \ - $(TEST_LIBC_STDIO_DEPS) \ - o/$(MODE)/test/libc/stdio/system_test.o \ - o/$(MODE)/test/libc/stdio/stdio.pkg \ - o/$(MODE)/tool/build/echo.com.zip.o \ - o/$(MODE)/tool/build/cocmd.com.zip.o \ - o/$(MODE)/tool/build/false.com.zip.o \ - $(LIBC_TESTMAIN) \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - o/$(MODE)/test/libc/stdio/popen_test.com.dbg: \ $(TEST_LIBC_STDIO_DEPS) \ o/$(MODE)/test/libc/stdio/popen_test.o \ @@ -85,25 +73,12 @@ o/$(MODE)/test/libc/stdio/popen_test.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/test/libc/stdio/posix_spawn_test.com.runs: \ - private QUOTA += -M8192m - -o/$(MODE)/test/libc/stdio/posix_spawn_test.com.dbg: \ - $(TEST_LIBC_STDIO_DEPS) \ - o/$(MODE)/test/libc/stdio/posix_spawn_test.o \ - o/$(MODE)/test/libc/stdio/stdio.pkg \ - o/$(MODE)/tool/build/echo.com.zip.o \ - o/$(MODE)/test/libc/mem/prog/life.com.zip.o \ - o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ - $(LIBC_TESTMAIN) \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - $(TEST_LIBC_STDIO_OBJS): private \ DEFAULT_CCFLAGS += \ -fno-builtin +$(TEST_LIBC_STDIO_OBJS): test/libc/stdio/test.mk + .PHONY: o/$(MODE)/test/libc/stdio o/$(MODE)/test/libc/stdio: \ $(TEST_LIBC_STDIO_BINS) \ diff --git a/test/libc/stdio/tmpfile_test.c b/test/libc/stdio/tmpfile_test.c index f96284642..3ab999910 100644 --- a/test/libc/stdio/tmpfile_test.c +++ b/test/libc/stdio/tmpfile_test.c @@ -31,7 +31,6 @@ #include "libc/testlib/testlib.h" #include "libc/x/xasprintf.h" -char testlib_enable_tmp_setup_teardown; char oldtmpdir[PATH_MAX]; bool IsDirectoryEmpty(const char *path) { @@ -48,24 +47,28 @@ bool IsDirectoryEmpty(const char *path) { return res; } +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + void SetUp(void) { - strcpy(oldtmpdir, kTmpPath); - strcpy(kTmpPath, "."); + strcpy(oldtmpdir, __get_tmpdir()); + strcpy(__get_tmpdir(), "."); } void TearDown(void) { - strcpy(kTmpPath, oldtmpdir); + strcpy(__get_tmpdir(), oldtmpdir); } TEST(tmpfile, test) { FILE *f; struct stat st; - EXPECT_TRUE(IsDirectoryEmpty(kTmpPath)); + EXPECT_TRUE(IsDirectoryEmpty(__get_tmpdir())); f = tmpfile(); if (IsWindows()) { - EXPECT_FALSE(IsDirectoryEmpty(kTmpPath)); + EXPECT_FALSE(IsDirectoryEmpty(__get_tmpdir())); } else { - EXPECT_TRUE(IsDirectoryEmpty(kTmpPath)); + EXPECT_TRUE(IsDirectoryEmpty(__get_tmpdir())); } EXPECT_SYS(0, 0, fstat(fileno(f), &st)); EXPECT_NE(010600, st.st_mode); @@ -74,7 +77,7 @@ TEST(tmpfile, test) { rewind(f); EXPECT_EQ('t', fgetc(f)); EXPECT_EQ(0, fclose(f)); - EXPECT_TRUE(IsDirectoryEmpty(kTmpPath)); + EXPECT_TRUE(IsDirectoryEmpty(__get_tmpdir())); } #ifndef __aarch64__ diff --git a/test/libc/stdio/ungetc_test.c b/test/libc/stdio/ungetc_test.c index 10f8c0085..5e78244d5 100644 --- a/test/libc/stdio/ungetc_test.c +++ b/test/libc/stdio/ungetc_test.c @@ -27,7 +27,10 @@ FILE *f; char buf[512]; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(ungetc, testGetChar_canBeUndoneWithinReason) { ASSERT_NE(NULL, (f = fopen("hog", "wb"))); diff --git a/test/libc/stdio/zipdir_test.c b/test/libc/stdio/zipdir_test.c index 4c1d42400..cd661cf22 100644 --- a/test/libc/stdio/zipdir_test.c +++ b/test/libc/stdio/zipdir_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/dirent.h" +#include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/macros.internal.h" #include "libc/mem/gc.internal.h" @@ -25,6 +26,7 @@ #include "libc/runtime/zipos.internal.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/dt.h" #include "libc/testlib/testlib.h" @@ -64,8 +66,10 @@ TEST(zipdir, test) { } TEST(dirstream, hasDirectoryEntry) { + struct stat st; bool gotsome = false; const char *path = "/zip/usr/share/zoneinfo"; + ASSERT_SYS(0, 0, fstatat(AT_FDCWD, path, &st, 0)); ASSERT_NE(NULL, (dir = opendir(path))); while ((ent = readdir(dir))) { gotsome = true; diff --git a/test/libc/str/crc32c_test.c b/test/libc/str/crc32c_test.c index 00f50ce92..e37fe9ff7 100644 --- a/test/libc/str/crc32c_test.c +++ b/test/libc/str/crc32c_test.c @@ -71,16 +71,18 @@ static unsigned KMH(const void *p, unsigned long n) { BENCH(crc32c, bench) { for (int i = 1; i < 256; i *= 2) { - EZBENCH_N("crc32c", i, crc32c(0, kHyperion, i)); - EZBENCH_N("crc32_z", i, crc32_z(0, kHyperion, i)); + EZBENCH_N("crc32c", i, __expropriate(crc32c(0, kHyperion, i))); + EZBENCH_N("crc32_z", i, __expropriate(crc32_z(0, kHyperion, i))); EZBENCH_N("fnv_hash", i, __expropriate(fnv_hash(__veil("r", kHyperion), __veil("r", i)))); EZBENCH_N("KMH", i, __expropriate(KMH(__veil("r", kHyperion), __veil("r", i)))); fprintf(stderr, "\n"); } - EZBENCH_N("crc32c", kHyperionSize, crc32c(0, kHyperion, kHyperionSize)); - EZBENCH_N("crc32_z", kHyperionSize, crc32_z(0, kHyperion, kHyperionSize)); + EZBENCH_N("crc32c", kHyperionSize, + __expropriate(crc32c(0, kHyperion, kHyperionSize))); + EZBENCH_N("crc32_z", kHyperionSize, + __expropriate(crc32_z(0, kHyperion, kHyperionSize))); EZBENCH_N("fnv_hash", kHyperionSize, __expropriate( fnv_hash(__veil("r", kHyperion), __veil("r", kHyperionSize)))); diff --git a/test/libc/str/highwayhash64_test.c b/test/libc/str/highwayhash64_test.c index 30cbb88c7..6991985e3 100644 --- a/test/libc/str/highwayhash64_test.c +++ b/test/libc/str/highwayhash64_test.c @@ -114,13 +114,15 @@ BENCH(highwayhash64, newbench) { BENCH(highwayhash64, bench) { EZBENCH2("knuth small", donothing, __expropriate(KnuthMultiplicativeHash32(__veil("r", "hello"), 5))); - EZBENCH2("crc32c small", donothing, crc32c(0, "hello", 5)); + EZBENCH2("crc32c small", donothing, __expropriate(crc32c(0, "hello", 5))); EZBENCH2("crc32 small", donothing, __expropriate(crc32_z(0, __veil("r", "hello"), 5))); EZBENCH2("highwayhash64 small", donothing, HighwayHash64((void *)"hello", 5, kTestKey1)); - EZBENCH2("crc32 big", donothing, crc32_z(0, kHyperion, kHyperionSize)); - EZBENCH2("crc32c big", donothing, crc32c(0, kHyperion, kHyperionSize)); + EZBENCH2("crc32 big", donothing, + __expropriate(crc32_z(0, kHyperion, kHyperionSize))); + EZBENCH2("crc32c big", donothing, + __expropriate(crc32c(0, kHyperion, kHyperionSize))); EZBENCH2("highwayhash64 big", donothing, HighwayHash64((void *)kHyperion, kHyperionSize, kTestKey1)); EZBENCH2("knuth big", donothing, diff --git a/test/libc/test.mk b/test/libc/test.mk index ef908621a..d21dcea45 100644 --- a/test/libc/test.mk +++ b/test/libc/test.mk @@ -10,6 +10,7 @@ o/$(MODE)/test/libc: \ o/$(MODE)/test/libc/log \ o/$(MODE)/test/libc/mem \ o/$(MODE)/test/libc/nexgen32e \ + o/$(MODE)/test/libc/proc \ o/$(MODE)/test/libc/release \ o/$(MODE)/test/libc/runtime \ o/$(MODE)/test/libc/sock \ diff --git a/test/libc/thread/async_test.c b/test/libc/thread/async_test.c new file mode 100644 index 000000000..c1ac8d639 --- /dev/null +++ b/test/libc/thread/async_test.c @@ -0,0 +1,70 @@ +/*-*- 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/mem/gc.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +int pfds[2]; +pthread_cond_t cv; +pthread_mutex_t mu; +atomic_int gotcleanup; +char *wouldleak; +pthread_key_t key; +bool key_destructor_was_run; +atomic_int is_in_infinite_loop; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + +void OnCleanup(void *arg) { + gotcleanup = true; +} + +void KeyDestructor(void *arg) { + CheckStackIsAligned(); + ASSERT_EQ(31337, (intptr_t)arg); + key_destructor_was_run = true; +} + +void *CancelSelfWorkerDeferred(void *arg) { + char buf[8]; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0); + pthread_cleanup_push(OnCleanup, 0); + pthread_cancel(pthread_self()); + read(pfds[0], buf, sizeof(buf)); + pthread_cleanup_pop(0); + return 0; +} + +TEST(pthread_cancel, self_deferred_waitsForCancellationPoint) { + void *rc; + pthread_t th; + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerDeferred, 0)); + ASSERT_EQ(0, pthread_join(th, &rc)); + ASSERT_EQ(PTHREAD_CANCELED, rc); + ASSERT_TRUE(gotcleanup); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); +} diff --git a/test/libc/thread/makecontext_test.c b/test/libc/thread/makecontext_test.c index fd4344bcf..0af39770f 100644 --- a/test/libc/thread/makecontext_test.c +++ b/test/libc/thread/makecontext_test.c @@ -35,7 +35,10 @@ bool gotsome; ucontext_t uc, goback; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void check_args(long x0, long x1, long x2, long x3, long x4, long x5, double f0, double f1, double f2, double f3, double f4, double f5) { diff --git a/test/libc/thread/mu_semaphore_sem_test.c b/test/libc/thread/mu_semaphore_sem_test.c new file mode 100644 index 000000000..055b4ffd8 --- /dev/null +++ b/test/libc/thread/mu_semaphore_sem_test.c @@ -0,0 +1,69 @@ +/*-*- 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/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +// create a bunch of threads with nsync semaphore waiter objects +// then fork and verify that unlocking in child safely cleans up +// this test matters on netbsd, where waiters have a file number + +#define N 10 + +pthread_t thread[N]; +atomic_uint countdown = N; +atomic_uint final_countdown = N; +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +void *WaitWorker(void *arg) { + ASSERT_EQ(0, pthread_mutex_lock(&lock)); + --countdown; + ASSERT_EQ(0, pthread_cond_wait(&cond, &lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&lock)); + ASSERT_EQ(0, pthread_mutex_lock(&lock)); + --final_countdown; + ASSERT_EQ(0, pthread_cond_wait(&cond, &lock)); + ASSERT_EQ(0, pthread_mutex_unlock(&lock)); + return 0; +} + +TEST(mu_semaphore_sem, test) { + for (int i = 0; i < N; ++i) { + ASSERT_EQ(0, pthread_create(thread + i, 0, WaitWorker, 0)); + } + while (countdown) pthread_yield(); + SPAWN(fork); + CheckForFileLeaks(); + EXITS(0); + ASSERT_EQ(0, pthread_mutex_lock(&lock)); + ASSERT_EQ(0, pthread_cond_broadcast(&cond)); + ASSERT_EQ(0, pthread_mutex_unlock(&lock)); + while (final_countdown) pthread_yield(); // make extra sure still works + ASSERT_EQ(0, pthread_mutex_lock(&lock)); + ASSERT_EQ(0, pthread_cond_broadcast(&cond)); + ASSERT_EQ(0, pthread_mutex_unlock(&lock)); + for (int i = 0; i < N; ++i) { + ASSERT_EQ(0, pthread_join(thread[i], 0)); + } + CheckForFileLeaks(); +} diff --git a/test/libc/thread/pthread_atfork_test.c b/test/libc/thread/pthread_atfork_test.c index 1333036c9..0799e2f3e 100644 --- a/test/libc/thread/pthread_atfork_test.c +++ b/test/libc/thread/pthread_atfork_test.c @@ -19,8 +19,10 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/mem/gc.h" +#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" @@ -75,7 +77,7 @@ void mu_unlock(void) { pthread_mutex_unlock(&mu); } -void mu_funlock(void) { +void mu_wipe(void) { pthread_mutex_init(&mu, 0); } @@ -97,11 +99,11 @@ void *Worker(void *arg) { return 0; } -TEST(pthread_atfork, torture) { - pthread_mutex_init(&mu, 0); - pthread_atfork(mu_lock, mu_unlock, mu_funlock); +TEST(pthread_atfork, fork_exit_torture) { + mu_wipe(); + pthread_atfork(mu_lock, mu_unlock, mu_wipe); int i, n = 4; - pthread_t *t = _gc(malloc(sizeof(pthread_t) * n)); + pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); for (i = 0; i < n; ++i) { ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0)); } diff --git a/test/libc/thread/pthread_barrier_wait_test.c b/test/libc/thread/pthread_barrier_wait_test.c index a0a17ec8a..f8ee812a2 100644 --- a/test/libc/thread/pthread_barrier_wait_test.c +++ b/test/libc/thread/pthread_barrier_wait_test.c @@ -22,15 +22,15 @@ #include "libc/intrin/atomic.h" #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" int i, n; atomic_int p, w; pthread_barrier_t barrier; -int Worker(void *arg, int tid) { +void *Worker(void *arg) { int rc; rc = pthread_barrier_wait(&barrier); atomic_fetch_add(&w, 1); @@ -42,31 +42,31 @@ int Worker(void *arg, int tid) { } TEST(pthread_barrier_wait, test1) { - struct spawn t; + pthread_t t; p = 0; w = 0; n = 1; ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); - ASSERT_SYS(0, 0, _spawn(Worker, 0, &t)); - EXPECT_SYS(0, 0, _join(&t)); + ASSERT_SYS(0, 0, pthread_create(&t, 0, Worker, 0)); + EXPECT_SYS(0, 0, pthread_join(t, 0)); ASSERT_EQ(1, p); ASSERT_EQ(n, w); ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); } TEST(pthread_barrier_wait, test32) { - struct spawn *t; + pthread_t *t; p = 0; - w = 0; n = 32; - t = gc(malloc(sizeof(struct spawn) * n)); + w = 0; + t = gc(malloc(sizeof(pthread_t) * n)); ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); for (i = 0; i < n; ++i) { ASSERT_EQ(0, w); - ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); + ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0)); } for (i = 0; i < n; ++i) { - EXPECT_SYS(0, 0, _join(t + i)); + EXPECT_SYS(0, 0, pthread_join(t[i], 0)); } ASSERT_EQ(1, p); ASSERT_EQ(n, w); diff --git a/test/libc/thread/pthread_cancel_test.c b/test/libc/thread/pthread_cancel_test.c index 8a947edb3..11e8fa011 100644 --- a/test/libc/thread/pthread_cancel_test.c +++ b/test/libc/thread/pthread_cancel_test.c @@ -35,7 +35,10 @@ int pfds[2]; pthread_cond_t cv; pthread_mutex_t mu; atomic_int gotcleanup; -char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} void SetUp(void) { gotcleanup = false; @@ -80,7 +83,7 @@ TEST(pthread_cancel, synchronous) { pthread_t th; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); - pthread_cancel(th); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_TRUE(gotcleanup); @@ -93,10 +96,10 @@ TEST(pthread_cancel, synchronous_delayed) { pthread_t th; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); - usleep(10); - pthread_cancel(th); - ASSERT_EQ(0, pthread_join(th, &rc)); - ASSERT_EQ(PTHREAD_CANCELED, rc); + ASSERT_SYS(0, 0, usleep(10)); + EXPECT_EQ(0, pthread_cancel(th)); + EXPECT_EQ(0, pthread_join(th, &rc)); + EXPECT_EQ(PTHREAD_CANCELED, rc); ASSERT_TRUE(gotcleanup); ASSERT_SYS(0, 0, close(pfds[1])); ASSERT_SYS(0, 0, close(pfds[0])); @@ -116,7 +119,7 @@ TEST(pthread_cancel, masked) { pthread_t th; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, DisabledWorker, 0)); - pthread_cancel(th); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, rc); ASSERT_FALSE(gotcleanup); @@ -129,8 +132,8 @@ TEST(pthread_cancel, masked_delayed) { pthread_t th; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, DisabledWorker, 0)); - usleep(10); - pthread_cancel(th); + ASSERT_SYS(0, 0, usleep(10)); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, rc); ASSERT_FALSE(gotcleanup); @@ -150,7 +153,7 @@ TEST(pthread_cancel, condMaskedWait) { void *rc; pthread_t th; ASSERT_EQ(0, pthread_create(&th, 0, CondWaitMaskedWorker, 0)); - pthread_cancel(th); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, rc); } @@ -159,14 +162,14 @@ TEST(pthread_cancel, condWaitMaskedDelayed) { void *rc; pthread_t th; ASSERT_EQ(0, pthread_create(&th, 0, CondWaitMaskedWorker, 0)); - usleep(10); - pthread_cancel(th); + ASSERT_SYS(0, 0, usleep(10)); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(0, rc); } void *CondWaitDeferredWorker(void *arg) { - pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, 0); + ASSERT_EQ(0, pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, 0)); ASSERT_EQ(0, pthread_mutex_lock(&mu)); ASSERT_EQ(ECANCELED, pthread_cond_timedwait(&cv, &mu, 0)); abort(); @@ -176,7 +179,7 @@ TEST(pthread_cancel, condDeferredWait_reacquiresMutex) { void *rc; pthread_t th; ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0)); - pthread_cancel(th); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_EQ(EBUSY, pthread_mutex_trylock(&mu)); @@ -187,8 +190,8 @@ TEST(pthread_cancel, condDeferredWaitDelayed) { void *rc; pthread_t th; ASSERT_EQ(0, pthread_create(&th, 0, CondWaitDeferredWorker, 0)); - usleep(10); - pthread_cancel(th); + ASSERT_SYS(0, 0, usleep(10)); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_EQ(EBUSY, pthread_mutex_trylock(&mu)); @@ -245,17 +248,12 @@ TEST(pthread_cancel, async) { void *rc; pthread_t th; ASSERT_EQ(0, pthread_key_create(&key, KeyDestructor)); - if (IsWindows()) { - // asynchronous cancellations aren't possible on windows yet - ASSERT_EQ(ENOTSUP, pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0)); - return; - } wouldleak = NULL; is_in_infinite_loop = false; key_destructor_was_run = false; ASSERT_EQ(0, pthread_create(&th, 0, CpuBoundWorker, 0)); while (!is_in_infinite_loop) pthread_yield(); - pthread_cancel(th); + ASSERT_EQ(0, pthread_cancel(th)); ASSERT_EQ(0, pthread_join(th, &rc)); ASSERT_EQ(PTHREAD_CANCELED, rc); ASSERT_TRUE(key_destructor_was_run); @@ -264,9 +262,9 @@ TEST(pthread_cancel, async) { } void *CancelSelfWorkerAsync(void *arg) { - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); + ASSERT_EQ(0, pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0)); pthread_cleanup_push(OnCleanup, 0); - pthread_cancel(pthread_self()); + ASSERT_EQ(0, pthread_cancel(pthread_self())); pthread_cleanup_pop(0); return 0; } @@ -274,7 +272,6 @@ void *CancelSelfWorkerAsync(void *arg) { TEST(pthread_cancel, self_asynchronous_takesImmediateEffect) { void *rc; pthread_t th; - if (IsWindows()) return; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerAsync, 0)); ASSERT_EQ(0, pthread_join(th, &rc)); diff --git a/test/libc/thread/pthread_cond_signal_test.c b/test/libc/thread/pthread_cond_signal_test.c index c2aca7b4b..59ec74599 100644 --- a/test/libc/thread/pthread_cond_signal_test.c +++ b/test/libc/thread/pthread_cond_signal_test.c @@ -22,8 +22,6 @@ #include "libc/thread/thread.h" #include "libc/thread/thread2.h" -// TODO(jart): Can we make this test go faster on NetBSD? - int pos; int count; int limit; diff --git a/test/libc/thread/pthread_create_test.c b/test/libc/thread/pthread_create_test.c index ce58c29f9..5c8c65e06 100644 --- a/test/libc/thread/pthread_create_test.c +++ b/test/libc/thread/pthread_create_test.c @@ -22,8 +22,10 @@ #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" +#include "libc/calls/struct/siginfo.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/mem/gc.h" @@ -33,6 +35,7 @@ #include "libc/nexgen32e/vendor.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" +#include "libc/runtime/sysconf.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sched.h" @@ -41,6 +44,7 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" #include "libc/thread/thread2.h" @@ -252,14 +256,14 @@ TEST(pthread_cleanup, pthread_normal) { } //////////////////////////////////////////////////////////////////////////////// -// HOW TO PROTECT YOUR THREADS FROM STACK OVERFLOW -// note that sigaltstack is waq on the main thread jmp_buf recover; volatile bool smashed_stack; -void CrashHandler(int sig) { +void CrashHandler(int sig, siginfo_t *si, void *ctx) { + kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr); smashed_stack = true; + ASSERT_TRUE(__is_stack_overflow(si, ctx)); longjmp(recover, 123); } @@ -276,14 +280,14 @@ int (*pStackOverflow)(int (*)(), int) = StackOverflow; void *MyPosixThread(void *arg) { int jumpcode; struct sigaction sa, o1, o2; - struct sigaltstack ss = { - .ss_sp = gc(malloc(SIGSTKSZ)), - .ss_size = SIGSTKSZ, - }; - sigaltstack(&ss, 0); - sa.sa_flags = SA_ONSTACK; // <-- important + struct sigaltstack ss; + ss.ss_flags = 0; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_sp = gc(malloc(ss.ss_size)); + ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important sigemptyset(&sa.sa_mask); - sa.sa_handler = CrashHandler; + sa.sa_sigaction = CrashHandler; sigaction(SIGBUS, &sa, &o1); sigaction(SIGSEGV, &sa, &o2); if (!(jumpcode = setjmp(recover))) { @@ -334,6 +338,6 @@ BENCH(pthread_create, bench) { EZBENCH2("CreateDetach", donothing, CreateDetach()); EZBENCH2("CreateDetached", donothing, CreateDetached()); while (!pthread_orphan_np()) { - pthread_decimate_np(); + _pthread_decimate(); } } diff --git a/test/libc/thread/pthread_detach_test.c b/test/libc/thread/pthread_detach_test.c index 7e3fc9768..9e28ca9f8 100644 --- a/test/libc/thread/pthread_detach_test.c +++ b/test/libc/thread/pthread_detach_test.c @@ -51,7 +51,7 @@ TEST(pthread_detach, testCreateReturn) { ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0)); ASSERT_EQ(0, pthread_detach(id)); while (!pthread_orphan_np()) { - pthread_decimate_np(); + _pthread_decimate(); } } @@ -63,6 +63,6 @@ TEST(pthread_detach, testDetachUponCreation) { ASSERT_EQ(0, pthread_create(&th, &attr, Increment, 0)); ASSERT_EQ(0, pthread_attr_destroy(&attr)); while (!pthread_orphan_np()) { - pthread_decimate_np(); + _pthread_decimate(); } } diff --git a/test/libc/thread/pthread_kill_test.c b/test/libc/thread/pthread_kill_test.c index a355f53b8..38a91cb69 100644 --- a/test/libc/thread/pthread_kill_test.c +++ b/test/libc/thread/pthread_kill_test.c @@ -21,53 +21,225 @@ #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sock/struct/sockaddr.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" +#include "libc/thread/tls.h" int fds[2]; -_Thread_local sig_atomic_t gotsig; +atomic_bool ready; +atomic_bool was_completed; +volatile pthread_t gottid; +_Thread_local atomic_int gotsig; void SetUp(void) { - if (IsXnuSilicon()) { - fprintf(stderr, "TODO(jart): Get pthread_kill() working on XNU silicon\n"); - exit(0); - } + gotsig = 0; + gottid = 0; + ready = false; + was_completed = false; + // strace_enabled(+1); +} + +void TearDown(void) { + CheckForFileLeaks(); } void OnSig(int sig) { - gotsig = 1; + gotsig = sig; + gottid = pthread_self(); } -void *ReadWorker(void *arg) { - char buf[8]; - ASSERT_SYS(EINTR, -1, read(fds[0], buf, 8)); - ASSERT_TRUE(gotsig); +void WaitUntilReady(void) { + while (!ready) pthread_yield(); + ASSERT_SYS(0, 0, usleep(1000)); +} + +void *SleepWorker(void *arg) { + ready = true; + ASSERT_SYS(EINTR, -1, usleep(30 * 1e6)); + ASSERT_EQ(SIGUSR1, gotsig); + was_completed = true; return 0; } -TEST(pthread_kill, canCancelReadOperation) { +TEST(pthread_kill, canInterruptSleepOperation) { + pthread_t t; + sighandler_t old = signal(SIGUSR1, OnSig); + ASSERT_EQ(0, pthread_create(&t, 0, SleepWorker, 0)); + WaitUntilReady(); + ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); + ASSERT_EQ(0, pthread_join(t, 0)); + ASSERT_TRUE(was_completed); + ASSERT_EQ(0, gotsig); + signal(SIGUSR1, old); +} + +void *ReadWorker(void *arg) { + char buf[8] = {0}; + ready = true; + ASSERT_SYS(EINTR, -1, read(fds[0], buf, 8)); + ASSERT_EQ(pthread_self(), gottid); + ASSERT_EQ(SIGUSR1, gotsig); + was_completed = true; + return 0; +} + +TEST(pthread_kill, canInterruptReadOperation) { pthread_t t; struct sigaction oldsa; struct sigaction sa = {.sa_handler = OnSig}; ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa)); ASSERT_SYS(0, 0, pipe(fds)); ASSERT_EQ(0, pthread_create(&t, 0, ReadWorker, 0)); - ASSERT_SYS(0, 0, usleep(100000)); // potentially flaky + WaitUntilReady(); ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); ASSERT_EQ(0, pthread_join(t, 0)); - ASSERT_FALSE(gotsig); + ASSERT_TRUE(was_completed); + ASSERT_EQ(t, gottid); + ASSERT_EQ(0, gotsig); ASSERT_SYS(0, 0, close(fds[0])); ASSERT_SYS(0, 0, close(fds[1])); ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0)); } +void *RestartReadWorker(void *arg) { + char buf; + ready = true; + ASSERT_SYS(0, 1, read(fds[0], &buf, 1)); + ASSERT_EQ(pthread_self(), gottid); + ASSERT_EQ(SIGUSR1, gotsig); + ASSERT_EQ('x', buf); + was_completed = true; + return 0; +} + +TEST(pthread_kill, canRestartReadOperation) { + pthread_t t; + signal(SIGUSR1, OnSig); + ASSERT_SYS(0, 0, pipe(fds)); + ASSERT_EQ(0, pthread_create(&t, 0, RestartReadWorker, 0)); + WaitUntilReady(); + ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); + ASSERT_SYS(0, 1, write(fds[1], "x", 1)); + ASSERT_EQ(0, pthread_join(t, 0)); + ASSERT_TRUE(was_completed); + ASSERT_EQ(t, gottid); + ASSERT_EQ(0, gotsig); + ASSERT_SYS(0, 0, close(fds[0])); + ASSERT_SYS(0, 0, close(fds[1])); + signal(SIGUSR1, SIG_DFL); +} + +void *SocketReadWorker(void *arg) { + char buf[8] = {0}; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ready = true; + ASSERT_SYS(EINTR, -1, read(fds[0], buf, 8)); + ASSERT_EQ(pthread_self(), gottid); + ASSERT_EQ(SIGUSR1, gotsig); + ASSERT_SYS(0, 0, close(3)); + was_completed = true; + return 0; +} + +TEST(pthread_kill, canInterruptSocketReadOperation) { + pthread_t t; + struct sigaction oldsa; + struct sigaction sa = {.sa_handler = OnSig}; + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa)); + ASSERT_EQ(0, pthread_create(&t, 0, SocketReadWorker, 0)); + WaitUntilReady(); + ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); + ASSERT_EQ(0, pthread_join(t, 0)); + ASSERT_TRUE(was_completed); + ASSERT_EQ(t, gottid); + ASSERT_EQ(0, gotsig); + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0)); +} + +void *SocketAcceptWorker(void *arg) { + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, listen(3, 1)); + ready = true; + ASSERT_SYS(EINTR, -1, accept(3, 0, 0)); + ASSERT_EQ(pthread_self(), gottid); + ASSERT_EQ(SIGUSR1, gotsig); + ASSERT_SYS(0, 0, close(3)); + was_completed = true; + return 0; +} + +TEST(pthread_kill, canInterruptSocketAcceptOperation) { + pthread_t t; + struct sigaction oldsa; + struct sigaction sa = {.sa_handler = OnSig}; + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa)); + ASSERT_EQ(0, pthread_create(&t, 0, SocketAcceptWorker, 0)); + WaitUntilReady(); + ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); + ASSERT_EQ(0, pthread_join(t, 0)); + ASSERT_TRUE(was_completed); + ASSERT_EQ(t, gottid); + ASSERT_EQ(0, gotsig); + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0)); +} + +void *WriteWorker(void *arg) { + char buf[2048] = {0}; + ready = true; + for (;;) { + int rc = write(fds[1], buf, 2048); + if (rc != 2048) { + ASSERT_EQ(-1, rc); + ASSERT_EQ(EINTR, errno); + ASSERT_EQ(SIGUSR2, gotsig); + was_completed = true; + return 0; + } + } +} + +TEST(pthread_kill, canCancelWriteOperation) { + pthread_t t; + struct sigaction oldsa; + struct sigaction sa = {.sa_handler = OnSig}; + ASSERT_SYS(0, 0, sigaction(SIGUSR2, &sa, &oldsa)); + ASSERT_SYS(0, 0, pipe(fds)); + ASSERT_EQ(0, pthread_create(&t, 0, WriteWorker, 0)); + WaitUntilReady(); + ASSERT_EQ(0, pthread_kill(t, SIGUSR2)); + ASSERT_EQ(0, pthread_join(t, 0)); + ASSERT_TRUE(was_completed); + ASSERT_EQ(0, gotsig); + ASSERT_SYS(0, 0, close(fds[0])); + ASSERT_SYS(0, 0, close(fds[1])); + ASSERT_SYS(0, 0, sigaction(SIGUSR2, &oldsa, 0)); +} + volatile unsigned got_sig_async; volatile pthread_t cpu_worker_th; volatile unsigned is_wasting_cpu; +volatile unsigned exited_original_loop; void OnSigAsync(int sig) { ASSERT_TRUE(pthread_equal(cpu_worker_th, pthread_self())); @@ -79,20 +251,25 @@ void *CpuWorker(void *arg) { while (!got_sig_async) { is_wasting_cpu = 1; } + exited_original_loop = 1; return 0; } TEST(pthread_kill, canAsynchronouslyRunHandlerInsideTargetThread) { + ASSERT_NE(0, __get_tls()->tib_tid); pthread_t t; struct sigaction oldsa; struct sigaction sa = {.sa_handler = OnSigAsync}; ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa)); ASSERT_EQ(0, pthread_create(&t, 0, CpuWorker, 0)); while (!is_wasting_cpu) donothing; - ASSERT_EQ(0, pthread_kill(t, SIGUSR1)); + EXPECT_EQ(0, pthread_kill(t, SIGUSR1)); ASSERT_EQ(0, pthread_join(t, 0)); ASSERT_TRUE(got_sig_async); + ASSERT_TRUE(exited_original_loop); ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0)); + ASSERT_EQ(0, gotsig); + ASSERT_NE(0, __get_tls()->tib_tid); } volatile int is_having_fun; @@ -106,6 +283,7 @@ void *FunWorker(void *arg) { } TEST(pthread_kill, defaultThreadSignalHandlerWillKillWholeProcess) { + ASSERT_NE(0, __get_tls()->tib_tid); SPAWN(fork); pthread_t t; ASSERT_EQ(0, pthread_create(&t, 0, FunWorker, 0)); @@ -113,4 +291,33 @@ TEST(pthread_kill, defaultThreadSignalHandlerWillKillWholeProcess) { ASSERT_SYS(0, 0, pthread_kill(t, SIGKILL)); for (;;) sched_yield(); TERMS(SIGKILL); + ASSERT_NE(0, __get_tls()->tib_tid); +} + +void *SuspendWorker(void *arg) { + sigset_t ss; + sigemptyset(&ss); + ASSERT_SYS(EINTR, -1, sigsuspend(&ss)); + return (void *)(long)gotsig; +} + +TEST(pthread_kill, canInterruptSigsuspend) { + ASSERT_NE(0, __get_tls()->tib_tid); + int tid; + void *res; + pthread_t t; + sigset_t ss, oldss; + sighandler_t oldsig; + sigemptyset(&ss); + sigaddset(&ss, SIGUSR1); + oldsig = signal(SIGUSR1, OnSig); + ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss)); + ASSERT_EQ(0, pthread_create(&t, 0, SuspendWorker, 0)); + ASSERT_EQ(0, pthread_getunique_np(t, &tid)); + ASSERT_SYS(0, 0, pthread_kill(t, SIGUSR1)); + ASSERT_EQ(0, pthread_join(t, &res)); + ASSERT_EQ(0, gotsig); + ASSERT_EQ(SIGUSR1, (intptr_t)res); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0)); + signal(SIGUSR1, oldsig); } diff --git a/test/libc/thread/pthread_rwlock_rdlock_test.c b/test/libc/thread/pthread_rwlock_rdlock_test.c index c29dbcfec..bc82cbec9 100644 --- a/test/libc/thread/pthread_rwlock_rdlock_test.c +++ b/test/libc/thread/pthread_rwlock_rdlock_test.c @@ -18,9 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/atomic.h" #include "libc/mem/gc.h" +#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #define ITERATIONS 50000 @@ -32,7 +32,7 @@ atomic_int writes; pthread_rwlock_t lock; pthread_barrier_t barrier; -int Reader(void *arg, int tid) { +void *Reader(void *arg) { pthread_barrier_wait(&barrier); for (int i = 0; i < ITERATIONS; ++i) { ASSERT_EQ(0, pthread_rwlock_rdlock(&lock)); @@ -42,7 +42,7 @@ int Reader(void *arg, int tid) { return 0; } -int Writer(void *arg, int tid) { +void *Writer(void *arg) { pthread_barrier_wait(&barrier); for (int i = 0; i < ITERATIONS; ++i) { ASSERT_EQ(0, pthread_rwlock_wrlock(&lock)); @@ -54,13 +54,14 @@ int Writer(void *arg, int tid) { TEST(pthread_rwlock_rdlock, test) { int i; - struct spawn *t = _gc(malloc(sizeof(struct spawn) * (READERS + WRITERS))); + pthread_t *t = gc(malloc(sizeof(pthread_t) * (READERS + WRITERS))); ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, READERS + WRITERS)); for (i = 0; i < READERS + WRITERS; ++i) { - ASSERT_SYS(0, 0, _spawn(i < READERS ? Reader : Writer, 0, t + i)); + ASSERT_SYS(0, 0, + pthread_create(t + i, 0, i < READERS ? Reader : Writer, 0)); } for (i = 0; i < READERS + WRITERS; ++i) { - EXPECT_SYS(0, 0, _join(t + i)); + EXPECT_SYS(0, 0, pthread_join(t[i], 0)); } EXPECT_EQ(READERS * ITERATIONS, reads); EXPECT_EQ(WRITERS * ITERATIONS, writes); diff --git a/test/libc/calls/setitimer_test.c b/test/libc/thread/setitimer_test.c similarity index 75% rename from test/libc/calls/setitimer_test.c rename to test/libc/thread/setitimer_test.c index 62a47f182..cdd17a7fb 100644 --- a/test/libc/calls/setitimer_test.c +++ b/test/libc/thread/setitimer_test.c @@ -16,6 +16,7 @@ │ 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/itimerval.h" #include "libc/calls/struct/sigaction.h" @@ -23,6 +24,10 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/timeval.h" #include "libc/calls/ucontext.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sa.h" @@ -32,35 +37,65 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -bool gotsig; +atomic_int gotsig; +atomic_int gottid; void SetUpOnce(void) { ASSERT_SYS(0, 0, pledge("stdio proc", 0)); } +void SetUp(void) { + gotsig = 0; +} + +TEST(setitimer, badTimer_isInvalid) { + struct itimerval it = {{0, 0}, {0, 200000}}; + ASSERT_SYS(EINVAL, -1, setitimer(INT_MIN, &it, 0)); +} + +TEST(setitimer, defaultActionIsToKillProcess) { + SPAWN(fork); + struct itimerval singleshot = {{0}, {0, 100}}; + ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &singleshot, 0)); + pause(); + TERMS(SIGALRM); +} + void OnSigAlrm(int sig, siginfo_t *si, void *ctx) { EXPECT_EQ(SIGALRM, sig); EXPECT_EQ(SIGALRM, si->si_signo); - gotsig = true; + gottid = gettid(); + gotsig = sig; +} + +TEST(setitimer, asynchronousDelivery) { + struct itimerval it = {{0, 0}, {0, 200000}}; + struct sigaction sa = {.sa_sigaction = OnSigAlrm, + .sa_flags = SA_RESETHAND | SA_SIGINFO}; + sigaction(SIGALRM, &sa, 0); + ASSERT_EQ(0, setitimer(ITIMER_REAL, &it, 0)); + while (!gotsig) { + } + ASSERT_EQ(gettid(), gottid); + ASSERT_EQ(SIGALRM, gotsig); + ASSERT_EQ(SIG_DFL, signal(SIGALRM, SIG_DFL)); } TEST(setitimer, testSingleShot) { sigset_t block, oldmask; - struct sigaction oldalrm; struct itimerval it = {{0, 0}, {0, 10000}}; struct sigaction sa = {.sa_sigaction = OnSigAlrm, .sa_flags = SA_RESETHAND | SA_SIGINFO}; - gotsig = false; sigemptyset(&block); + sigaction(SIGALRM, &sa, 0); sigaddset(&block, SIGALRM); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &block, &oldmask)); - ASSERT_EQ(0, sigaction(SIGALRM, &sa, &oldalrm)); ASSERT_EQ(0, setitimer(ITIMER_REAL, &it, 0)); sigdelset(&block, SIGALRM); EXPECT_EQ(-1, sigsuspend(&block)); EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &oldmask, 0)); - EXPECT_EQ(0, sigaction(SIGUSR1, &oldalrm, 0)); - EXPECT_EQ(true, gotsig); + ASSERT_EQ(gettid(), gottid); + ASSERT_EQ(SIGALRM, gotsig); } TEST(setitimer, notInheritedAcrossFork) { diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk index f9a6bc015..9c0a662b7 100644 --- a/test/libc/thread/test.mk +++ b/test/libc/thread/test.mk @@ -26,9 +26,12 @@ TEST_LIBC_THREAD_DIRECTDEPS = \ LIBC_CALLS \ LIBC_FMT \ LIBC_INTRIN \ + LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ + LIBC_SOCK \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ @@ -60,6 +63,9 @@ o/$(MODE)/test/libc/thread/pthread_create_test.o: \ private CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED +o/$(MODE)/test/libc/thread/pthread_kill_test.com.runs: \ + private .PLEDGE = stdio rpath wpath cpath fattr proc inet + .PHONY: o/$(MODE)/test/libc/thread o/$(MODE)/test/libc/thread: \ $(TEST_LIBC_THREAD_BINS) \ diff --git a/test/libc/x/xslurp_test.c b/test/libc/x/xslurp_test.c index be48792f0..5d3c5b3f7 100644 --- a/test/libc/x/xslurp_test.c +++ b/test/libc/x/xslurp_test.c @@ -22,7 +22,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(xslurp, testEmptyWithNulTerminatedStringBehavior) { size_t got; diff --git a/test/tool/build/lib/getargs_test.c b/test/tool/build/lib/getargs_test.c index c73eb8356..c6a13aa0c 100644 --- a/test/tool/build/lib/getargs_test.c +++ b/test/tool/build/lib/getargs_test.c @@ -16,15 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/lib/getargs.h" #include "libc/calls/calls.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -#include "tool/build/lib/getargs.h" -char testlib_enable_tmp_setup_teardown; +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} TEST(getargs, test) { struct GetArgs ga; diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index afc40c65a..9df150d0e 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -44,14 +44,14 @@ __static_yoink("zipos"); __static_yoink("o/" MODE "/test/tool/net/redbean-tester.com"); -char testlib_enable_tmp_setup_teardown_once; int port; void SetUpOnce(void) { - if (IsWindows()) return; ssize_t n; char buf[1024]; int fdin, fdout; + if (IsWindows()) return; + testlib_enable_tmp_setup_teardown_once(); ASSERT_NE(-1, mkdir("bin", 0755)); ASSERT_NE(-1, (fdin = open("/zip/o/" MODE "/test/tool/net/redbean-tester.com", O_RDONLY))); diff --git a/test/tool/net/sqlite_test.c b/test/tool/net/sqlite_test.c index f10e79746..d2266460f 100644 --- a/test/tool/net/sqlite_test.c +++ b/test/tool/net/sqlite_test.c @@ -27,9 +27,8 @@ #include "libc/thread/thread.h" #include "third_party/sqlite3/sqlite3.h" -char testlib_enable_tmp_setup_teardown; - void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); sqlite3_initialize(); } diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index 43c97a8ad..26e52edc9 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -35,6 +35,7 @@ TEST_TOOL_NET_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/test/tool/plinko/plinko_test.c b/test/tool/plinko/plinko_test.c index 26dcd5767..e43ea6535 100644 --- a/test/tool/plinko/plinko_test.c +++ b/test/tool/plinko/plinko_test.c @@ -49,11 +49,10 @@ static const char *const kSauces[] = { "/zip/ok.lisp", // }; -char testlib_enable_tmp_setup_teardown_once; - void SetUpOnce(void) { exit(0); // TODO(jart): How can we safely disable TLS with *NSYNC? int fdin, fdout; + testlib_enable_tmp_setup_teardown_once(); ASSERT_NE(-1, mkdir("bin", 0755)); ASSERT_NE(-1, (fdin = open("/zip/plinko.com", O_RDONLY))); ASSERT_NE(-1, (fdout = creat("bin/plinko.com", 0755))); diff --git a/test/tool/plinko/test.mk b/test/tool/plinko/test.mk index a3036611c..431eb914b 100644 --- a/test/tool/plinko/test.mk +++ b/test/tool/plinko/test.mk @@ -45,6 +45,7 @@ TEST_TOOL_PLINKO_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/third_party/awk/awk.mk b/third_party/awk/awk.mk index 0cff336c0..38170dd8d 100644 --- a/third_party/awk/awk.mk +++ b/third_party/awk/awk.mk @@ -13,15 +13,16 @@ THIRD_PARTY_AWK_SRCS = $(filter %.c,$(THIRD_PARTY_AWK_FILES)) THIRD_PARTY_AWK_OBJS = $(THIRD_PARTY_AWK_SRCS:%.c=o/$(MODE)/%.o) THIRD_PARTY_AWK_A_DIRECTDEPS = \ + LIBC_CALLS \ LIBC_FMT \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ - LIBC_CALLS \ LIBC_STDIO \ - LIBC_SYSV \ LIBC_STR \ + LIBC_SYSV \ LIBC_TINYMATH \ TOOL_ARGS \ THIRD_PARTY_GDTOA diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c index 50a4a11da..bdf07f1cc 100644 --- a/third_party/chibicc/chibicc.c +++ b/third_party/chibicc/chibicc.c @@ -380,7 +380,7 @@ static char *replace_extn(char *tmpl, char *extn) { } static char *create_tmpfile(void) { - char *path = xjoinpaths(kTmpPath, "chibicc-XXXXXX"); + char *path = xjoinpaths(__get_tmpdir(), "chibicc-XXXXXX"); int fd = mkstemp(path); if (fd == -1) error("%s: mkstemp failed: %s", path, strerror(errno)); close(fd); diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index 0789da7f4..3e4c2763f 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -54,6 +54,7 @@ THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/third_party/ctags/ctags.mk b/third_party/ctags/ctags.mk index 488b6a580..4ba5469df 100644 --- a/third_party/ctags/ctags.mk +++ b/third_party/ctags/ctags.mk @@ -20,6 +20,7 @@ THIRD_PARTY_CTAGS_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ + LIBC_PROC \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index 514b9edd5..9b4786587 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -50,19 +50,14 @@ o/$(MODE)/third_party/dlmalloc/dlmalloc.o: private \ endif endif -# we can't use address sanitizer because: -# address sanitizer depends on dlmalloc -o/$(MODE)/third_party/dlmalloc/dlmalloc.o: private \ - CFLAGS += \ +$(THIRD_PARTY_DLMALLOC_A_OBJS): private \ + COPTS += \ -ffreestanding \ - -fno-sanitize=address - -# we must segregate codegen because: -# file contains multiple independently linkable apis -o/$(MODE)/third_party/dlmalloc/dlmalloc.o: private \ - CFLAGS += \ + -fdata-sections \ -ffunction-sections \ - -fdata-sections + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 THIRD_PARTY_DLMALLOC_LIBS = $(foreach x,$(THIRD_PARTY_DLMALLOC_ARTIFACTS),$($(x))) THIRD_PARTY_DLMALLOC_SRCS = $(foreach x,$(THIRD_PARTY_DLMALLOC_ARTIFACTS),$($(x)_SRCS)) diff --git a/third_party/ggml/ggml.c b/third_party/ggml/ggml.c index 98d7c4841..ae5cba95d 100644 --- a/third_party/ggml/ggml.c +++ b/third_party/ggml/ggml.c @@ -49,6 +49,7 @@ #include "libc/assert.h" #include "third_party/ggml/ggml.h" #include "libc/intrin/bsr.h" +#include "libc/thread/thread.h" #include "third_party/libcxx/math.h" asm(".ident\t\"\\n\\n\ @@ -3386,7 +3387,7 @@ inline static void ggml_critical_section_start(void) { while (processing > 0) { // wait for other threads to finish atomic_fetch_sub(&g_state_barrier, 1); - sched_yield(); // TODO: reconsider this + pthread_yield(); // TODO: reconsider this processing = atomic_fetch_add(&g_state_barrier, 1); } } diff --git a/third_party/libcxx/__threading_support b/third_party/libcxx/__threading_support index b67666d84..52ba3a9eb 100644 --- a/third_party/libcxx/__threading_support +++ b/third_party/libcxx/__threading_support @@ -14,6 +14,7 @@ #include "third_party/libcxx/chrono" #include "third_party/libcxx/iosfwd" #include "libc/thread/thread2.h" +#include "libc/thread/thread.h" #include "third_party/libcxx/errno.h" #ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER @@ -361,7 +362,7 @@ int __libcpp_thread_detach(__libcpp_thread_t *__t) void __libcpp_thread_yield() { - sched_yield(); + pthread_yield(); } void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 10951cadb..f579dd7e4 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -178,7 +178,7 @@ Copyright 2018-2020 Justine Tunney \\n\ Copyright 2010-2016 Salvatore Sanfilippo \\n\ Copyright 2010-2013 Pieter Noordhuis \""); -#define LINENOISE_POLL_MS __SIG_POLLING_INTERVAL_MS +#define LINENOISE_POLL_MS 50 #define LINENOISE_MAX_RING 8 #define LINENOISE_MAX_DEBUG 16 diff --git a/third_party/lua/loslib.c b/third_party/lua/loslib.c index 979a000d3..7022712f8 100644 --- a/third_party/lua/loslib.c +++ b/third_party/lua/loslib.c @@ -31,12 +31,12 @@ #include "libc/calls/weirdtypes.h" #include "libc/errno.h" #include "libc/runtime/runtime.h" -#include "libc/temp.h" +#include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/sysv/consts/exit.h" +#include "libc/temp.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" -#include "libc/str/locale.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" @@ -142,7 +142,7 @@ asm(".include \"libc/disclaimer.inc\""); #define LUA_TMPNAMBUFSIZE 32 #define lua_tmpnam(b,e) { \ - strcpy(b, kTmpPath); \ + strcpy(b, __get_tmpdir()); \ strcat(b, "lua_XXXXXX"); \ e = mkstemp(b); \ if (e != -1) close(e); \ diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 73da76bea..8abd0148d 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -121,6 +121,7 @@ THIRD_PARTY_LUA_A_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ @@ -190,6 +191,7 @@ THIRD_PARTY_LUA_UNIX_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index 139325c08..1c24bfe91 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -2013,7 +2013,7 @@ static int LuaUnixTiocgwinsz(lua_State *L) { // unix.sched_yield() static int LuaUnixSchedYield(lua_State *L) { - sched_yield(); + pthread_yield(); return 0; } diff --git a/third_party/make/job.c b/third_party/make/job.c index efc4da0a1..ce1d7cd80 100644 --- a/third_party/make/job.c +++ b/third_party/make/job.c @@ -415,7 +415,7 @@ get_tmpdir (struct file *file) { const char *tmpdir; tmpdir = get_target_variable (STRING_SIZE_TUPLE ("TMPDIR"), file, 0); - if (!tmpdir) tmpdir = kTmpPath; + if (!tmpdir) tmpdir = __get_tmpdir(); return strdup (tmpdir); } @@ -2117,40 +2117,6 @@ child_execute_job (struct childbase *child, } } - /* [jart] Prevent builds from talking to the Internet. */ - if (internet) - DB (DB_JOBS, (_("Allowing Internet access\n"))); - else if (!(~ipromises & (1ul << PROMISE_INET)) && - !(~ipromises & (1ul << PROMISE_DNS))) - DB (DB_JOBS, (_("Internet access will be blocked by pledge\n"))); -#ifdef __x86_64__ - else - { - e = errno; - if (!nointernet()) - DB (DB_JOBS, (_("Blocked Internet access with seccomp ptrace\n"))); - else - { - if (errno == EPERM) - { - errno = e; - DB (DB_JOBS, (_("Can't block Internet if already traced\n"))); - } - else if (errno == ENOSYS) - { - errno = e; - DB (DB_JOBS, (_("Need SECCOMP ptrace() to block Internet\n"))); - } - else - { - OSS (error, NILF, "%s: failed to block internet access: %s", - argv[0], strerror (errno)); - _Exit (127); - } - } - } -#endif - /* [jart] Resolve command into executable path. */ if (!strict || !sandboxed) { diff --git a/third_party/make/main.c b/third_party/make/main.c index a359ac61d..2bcd304e8 100644 --- a/third_party/make/main.c +++ b/third_party/make/main.c @@ -971,6 +971,8 @@ reset_jobserver (void) int main (int argc, char **argv, char **envp) { + ShowCrashReports(); + static char *stdin_nm = 0; int makefile_status = MAKE_SUCCESS; struct goaldep *read_files; diff --git a/third_party/make/make.mk b/third_party/make/make.mk index 8b22bba37..760a17dde 100644 --- a/third_party/make/make.mk +++ b/third_party/make/make.mk @@ -115,6 +115,7 @@ THIRD_PARTY_MAKE_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/third_party/mbedtls/README.cosmo b/third_party/mbedtls/README.cosmo index 82a3f3db8..42d4feba3 100644 --- a/third_party/mbedtls/README.cosmo +++ b/third_party/mbedtls/README.cosmo @@ -14,6 +14,8 @@ LICENSE LOCAL CHANGES + - Support ECANCELED (similar to EINTR) needed by pthread_cancel() + - Strengthened server against DOS by removing expensive protections for old Internet Explorer against Lucky Thirteen timing attacks. diff --git a/third_party/mbedtls/error.c b/third_party/mbedtls/error.c index a109b9b1e..21d91c2d3 100644 --- a/third_party/mbedtls/error.c +++ b/third_party/mbedtls/error.c @@ -15,6 +15,7 @@ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "third_party/mbedtls/error.h" #include "libc/fmt/fmt.h" #include "libc/str/str.h" #include "third_party/mbedtls/aes.h" @@ -31,7 +32,6 @@ #include "third_party/mbedtls/dhm.h" #include "third_party/mbedtls/ecp.h" #include "third_party/mbedtls/entropy.h" -#include "third_party/mbedtls/error.h" #include "third_party/mbedtls/gcm.h" #include "third_party/mbedtls/hkdf.h" #include "third_party/mbedtls/hmac_drbg.h" @@ -358,6 +358,8 @@ const char * mbedtls_high_level_strerr( int error_code ) return( "SSL - Connection requires a write call" ); case -(MBEDTLS_ERR_SSL_TIMEOUT): return( "SSL - The operation timed out" ); + case -(MBEDTLS_ERR_SSL_CANCELED): + return( "SSL - The POSIX thread was canceled" ); case -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT): return( "SSL - The client initiated a reconnect from the same port" ); case -(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD): diff --git a/third_party/mbedtls/net_sockets.c b/third_party/mbedtls/net_sockets.c index 68dc8a240..6ab8bcece 100644 --- a/third_party/mbedtls/net_sockets.c +++ b/third_party/mbedtls/net_sockets.c @@ -93,7 +93,11 @@ int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, break; } close(ctx->fd); - ret = MBEDTLS_ERR_NET_CONNECT_FAILED; + if (errno == ECANCELED) { + ret = MBEDTLS_ERR_SSL_CANCELED; + } else { + ret = MBEDTLS_ERR_NET_CONNECT_FAILED; + } } freeaddrinfo(addr_list); return ret; @@ -244,6 +248,7 @@ int mbedtls_net_accept(mbedtls_net_context *bind_ctx, #endif } if (ret < 0) { + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; if (net_would_block(bind_ctx) != 0) return MBEDTLS_ERR_SSL_WANT_READ; return MBEDTLS_ERR_NET_ACCEPT_FAILED; } @@ -252,8 +257,10 @@ int mbedtls_net_accept(mbedtls_net_context *bind_ctx, if (type != SOCK_STREAM) { struct sockaddr_storage local_addr; int one = 1; - if (connect(bind_ctx->fd, (struct sockaddr *)&client_addr, n) != 0) + if (connect(bind_ctx->fd, (struct sockaddr *)&client_addr, n) != 0) { + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; return MBEDTLS_ERR_NET_ACCEPT_FAILED; + } client_ctx->fd = bind_ctx->fd; bind_ctx->fd = -1; /* In case we exit early */ n = sizeof(struct sockaddr_storage); @@ -369,7 +376,10 @@ int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) { ret = select(fd + 1, &read_fds, &write_fds, NULL, timeout == (uint32_t)-1 ? NULL : &tv); } while (IS_EINTR(ret)); - if (ret < 0) return MBEDTLS_ERR_NET_POLL_FAILED; + if (ret < 0) { + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; + return MBEDTLS_ERR_NET_POLL_FAILED; + } ret = 0; if (FD_ISSET(fd, &read_fds)) ret |= MBEDTLS_NET_POLL_READ; if (FD_ISSET(fd, &write_fds)) ret |= MBEDTLS_NET_POLL_WRITE; @@ -410,6 +420,7 @@ int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) { if (errno == EPIPE || errno == ECONNRESET) return MBEDTLS_ERR_NET_CONN_RESET; if (errno == EINTR) return MBEDTLS_ERR_SSL_WANT_READ; + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; return MBEDTLS_ERR_NET_RECV_FAILED; } return ret; @@ -462,6 +473,7 @@ int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, if (ret == 0) return MBEDTLS_ERR_SSL_TIMEOUT; if (ret < 0) { if (errno == EINTR) return MBEDTLS_ERR_SSL_WANT_READ; + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; return MBEDTLS_ERR_NET_RECV_FAILED; } /* This call will not block */ @@ -490,6 +502,7 @@ int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len) { if (errno == EPIPE || errno == ECONNRESET) return MBEDTLS_ERR_NET_CONN_RESET; if (errno == EINTR) return MBEDTLS_ERR_SSL_WANT_WRITE; + if (errno == ECANCELED) return MBEDTLS_ERR_SSL_CANCELED; return MBEDTLS_ERR_NET_SEND_FAILED; } return ret; diff --git a/third_party/mbedtls/ssl.h b/third_party/mbedtls/ssl.h index 8bc35e19d..3ffa5c4bf 100644 --- a/third_party/mbedtls/ssl.h +++ b/third_party/mbedtls/ssl.h @@ -61,6 +61,7 @@ COSMOPOLITAN_C_START_ #define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980 /*< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */ #define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /*< No data of requested type currently available on underlying transport. */ #define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /*< Connection requires a write call. */ +#define MBEDTLS_ERR_SSL_CANCELED -0x9900 /*< The POSIX thread was canceled. */ #define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /*< The operation timed out. */ #define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /*< The client initiated a reconnect from the same port. */ #define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 /*< Record header looks valid but is not expected. */ diff --git a/third_party/mbedtls/test/lib.c b/third_party/mbedtls/test/lib.c index 77add2c7b..af4153e27 100644 --- a/third_party/mbedtls/test/lib.c +++ b/third_party/mbedtls/test/lib.c @@ -17,6 +17,7 @@ #include "third_party/mbedtls/test/lib.h" #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" @@ -963,7 +964,7 @@ static void write_outcome_result(FILE *outcome_file, size_t unmet_dep_count, */ int execute_tests(int argc, const char **argv, const char *default_filename) { /* Local Configurations and options */ - long double t1, t2; + struct timespec t1, t2; const char *test_filename = NULL; const char **test_files = NULL; size_t testfile_count = 0; @@ -1089,7 +1090,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { sizeof(params) / sizeof(params[0])); } // If there are no unmet dependencies execute the test - t1 = nowl(); + t1 = timespec_real(); if (unmet_dep_count == 0) { mbedtls_test_info_reset(); function_id = strtoul(params[0], NULL, 10); @@ -1100,7 +1101,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { } } } - t2 = nowl(); + t2 = timespec_real(); write_outcome_result(outcome_file, unmet_dep_count, unmet_dependencies, missing_unmet_dependencies, ret, &mbedtls_test_info); if (unmet_dep_count > 0 || ret == DISPATCH_UNSUPPORTED_SUITE) { @@ -1120,7 +1121,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { missing_unmet_dependencies = 0; } else if (ret == DISPATCH_TEST_SUCCESS) { if (mbedtls_test_info.result == MBEDTLS_TEST_RESULT_SUCCESS) { - WRITE("PASS (%,ldus)\n", (int64_t)((t2 - t1) * 1e6)); + WRITE("PASS (%,ldus)\n", timespec_tomicros(timespec_sub(t2, t1))); } else if (mbedtls_test_info.result == MBEDTLS_TEST_RESULT_SKIPPED) { WRITE("----"); total_skipped++; diff --git a/third_party/musl/musl.mk b/third_party/musl/musl.mk index d326e3b3b..43b36da26 100644 --- a/third_party/musl/musl.mk +++ b/third_party/musl/musl.mk @@ -44,6 +44,9 @@ $(THIRD_PARTY_MUSL_A).pkg: \ $(THIRD_PARTY_MUSL_A_OBJS) \ $(foreach x,$(THIRD_PARTY_MUSL_A_DIRECTDEPS),$($(x)_A).pkg) +# offer assurances about the stack safety of cosmo libc +$(THIRD_PARTY_MUSL_A_OBJS): private COPTS += -Wframe-larger-than=4096 -Walloca-larger-than=4096 + THIRD_PARTY_MUSL_LIBS = $(foreach x,$(THIRD_PARTY_MUSL_ARTIFACTS),$($(x))) THIRD_PARTY_MUSL_SRCS = $(foreach x,$(THIRD_PARTY_MUSL_ARTIFACTS),$($(x)_SRCS)) THIRD_PARTY_MUSL_CHECKS = $(foreach x,$(THIRD_PARTY_MUSL_ARTIFACTS),$($(x)_CHECKS)) diff --git a/third_party/musl/tempnam.c b/third_party/musl/tempnam.c index 2825d7d2f..c666178e8 100644 --- a/third_party/musl/tempnam.c +++ b/third_party/musl/tempnam.c @@ -67,7 +67,7 @@ tempnam(const char *dir, const char *pfx) int i, r; char s[PATH_MAX]; size_t l, dl, pl; - if (!dir) dir = kTmpPath; + if (!dir) dir = __get_tmpdir(); if (!pfx) pfx = "temp"; dl = strlen(dir); pl = strlen(pfx); diff --git a/third_party/nsync/README.cosmo b/third_party/nsync/README.cosmo index bd79cc03b..d1d0eb4d7 100644 --- a/third_party/nsync/README.cosmo +++ b/third_party/nsync/README.cosmo @@ -15,9 +15,11 @@ ORIGIN LOCAL CHANGES - - Time APIs were so good that they're now part of our libc + - Time APIs were so good that they're now in libc - - Double linked list API was so good that it's now part of our libc + - Double linked list API was so good that it's now in libc + + - Ensure resources such as POSIX semaphores are are released on fork. - Modified *NSYNC to allocate waiter objects on the stack. We need it because we use *NSYNC mutexes to implement POSIX mutexes, which are @@ -27,10 +29,11 @@ LOCAL CHANGES it works well with Cosmopolitan's fat runtime portability. *NSYNC's unit test suite passes on all supported platforms. However the BSDs currently appear to be overutilizing CPU time compared with others. + This appears to be the fault of the OSes rather than *NSYNC / Cosmo - Support POSIX thread cancellation. APIs that wait on condition vars are now cancellation points. In PTHREAD_CANCEL_MASKED mode they may return ECANCELED. In PTHREAD_CANCEL_DEFERRED mode the POSIX threads - library will unwind the stack to unlock any locks and free waiters. + library will unwind the stack to re-acquire locks and free waiters. On the other hand the *NSYNC APIs for mutexes will now safely block thread cancellation, but you can still use *NSYNC notes to do that. diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index 797bb3a5c..9d6b47268 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -42,6 +42,7 @@ #include "libc/sysv/errfuns.h" #include "libc/thread/freebsd.internal.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" #include "third_party/nsync/atomic.h" #include "third_party/nsync/common.internal.h" @@ -132,7 +133,7 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ab } nanos = 100; - maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000; + maxnanos = __SIG_LOCK_INTERVAL_MS * 1000L * 1000; for (;;) { if (atomic_load_explicit (w, memory_order_acquire) != expect) { return 0; @@ -163,8 +164,11 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ab return -ETIMEDOUT; } -static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, const struct timespec *timeout) { +static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, + const struct timespec *timeout, + struct PosixThread *pt) { int rc; + bool32 ok; struct timespec deadline, interval, remain, wait, now; if (timeout) { @@ -180,12 +184,15 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, cons break; } remain = timespec_sub (deadline, now); - interval = timespec_frommillis (__SIG_POLLING_INTERVAL_MS); + interval = timespec_frommillis (__SIG_LOCK_INTERVAL_MS); wait = timespec_cmp (remain, interval) > 0 ? interval : remain; if (atomic_load_explicit (w, memory_order_acquire) != expect) { break; } - if (WaitOnAddress (w, &expect, sizeof(int), timespec_tomillis (wait))) { + if (pt) atomic_store_explicit (&pt->pt_futex, w, memory_order_release); + ok = WaitOnAddress (w, &expect, sizeof(int), timespec_tomillis (wait)); + if (pt) atomic_store_explicit (&pt->pt_futex, 0, memory_order_release); + if (ok) { break; } else { ASSERT (GetLastError () == ETIMEDOUT); @@ -212,7 +219,8 @@ static struct timespec *nsync_futex_timeout_ (struct timespec *memory, int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct timespec *abstime) { int e, rc, op; - struct PosixThread *pt = 0; + struct CosmoTib *tib; + struct PosixThread *pt; struct timespec tsmem, *timeout; cosmo_once (&nsync_futex_.once, nsync_futex_init_); @@ -239,12 +247,15 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time DescribeFutexOp (op), expect, DescribeTimespec (0, timeout)); + tib = __get_tls(); + pt = (struct PosixThread *)tib->tib_pthread; + if (nsync_futex_.is_supported) { e = errno; if (IsWindows ()) { // Windows 8 futexes don't support multiple processes :( if (pshare) goto Polyfill; - rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout); + rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt); } else if (IsFreebsd ()) { rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout); } else { @@ -255,9 +266,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time // unfortunately OpenBSD futex() defines // its own ECANCELED condition, and that // overlaps with our system call wrapper - if ((pt = (struct PosixThread *)__get_tls()->tib_pthread)) { - pt->flags &= ~PT_OPENBSD_KLUDGE; - } + if (pt) pt->pt_flags &= ~PT_OPENBSD_KLUDGE; } rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_); if (IsOpenbsd()) { @@ -270,7 +279,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time // because a SA_RESTART signal handler was // invoked, such as our SIGTHR callback. if (rc == -1 && errno == ECANCELED && - pt && (~pt->flags & PT_OPENBSD_KLUDGE)) { + pt && (~pt->pt_flags & PT_OPENBSD_KLUDGE)) { errno = EINTR; } } @@ -281,9 +290,9 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time } } else { Polyfill: - __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL; + tib->tib_flags |= TIB_FLAG_TIME_CRITICAL; rc = nsync_futex_polyfill_ (w, expect, timeout); - __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL; + tib->tib_flags &= ~TIB_FLAG_TIME_CRITICAL; } Finished: @@ -331,13 +340,13 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) { } } else { Polyfill: - sched_yield (); + pthread_yield (); rc = 0; } - STRACE ("futex(%t [%d], %s, %d) → %s", + STRACE ("futex(%t [%d], %s, %d) → %d woken", w, atomic_load_explicit (w, memory_order_relaxed), - DescribeFutexOp (op), count, DescribeErrno (rc)); + DescribeFutexOp (op), count, rc); return rc; } diff --git a/third_party/nsync/mem/mem.mk b/third_party/nsync/mem/mem.mk index fa0f19cab..a8c550b3a 100644 --- a/third_party/nsync/mem/mem.mk +++ b/third_party/nsync/mem/mem.mk @@ -38,10 +38,17 @@ $(THIRD_PARTY_NSYNC_MEM_A).pkg: \ $(THIRD_PARTY_NSYNC_MEM_A_OBJS) \ $(foreach x,$(THIRD_PARTY_NSYNC_MEM_A_DIRECTDEPS),$($(x)_A).pkg) -$(THIRD_PARTY_NSYNC_MEM_A_OBJS): private \ - CCFLAGS += \ - -ffunction-sections \ - -fdata-sections +# offer assurances about the stack safety of cosmo libc +$(THIRD_PARTY_NSYNC_MEM_A_OBJS): private COPTS += -Wframe-larger-than=4096 -Walloca-larger-than=4096 + +$(THIRD_PARTY_NSYNC_MEM_A_OBJS): private \ + COPTS += \ + -ffreestanding \ + -fdata-sections \ + -ffunction-sections \ + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 THIRD_PARTY_NSYNC_MEM_LIBS = $(foreach x,$(THIRD_PARTY_NSYNC_MEM_ARTIFACTS),$($(x))) THIRD_PARTY_NSYNC_MEM_SRCS = $(foreach x,$(THIRD_PARTY_NSYNC_MEM_ARTIFACTS),$($(x)_SRCS)) diff --git a/third_party/nsync/mu_semaphore.c b/third_party/nsync/mu_semaphore.c index 000ca90cd..6341b5c5c 100644 --- a/third_party/nsync/mu_semaphore.c +++ b/third_party/nsync/mu_semaphore.c @@ -46,7 +46,10 @@ void nsync_mu_semaphore_destroy (nsync_semaphore *s) { } } -/* Wait until the count of *s exceeds 0, and decrement it. */ +/* Wait until the count of *s exceeds 0, and decrement it. If POSIX cancellations + are currently disabled by the thread, then this function always succeeds. When + they're enabled in MASKED mode, this function may return ECANCELED. Otherwise, + cancellation will occur by unwinding cleanup handlers pushed to the stack. */ errno_t nsync_mu_semaphore_p (nsync_semaphore *s) { errno_t err; BEGIN_CANCELLATION_POINT; @@ -61,9 +64,10 @@ errno_t nsync_mu_semaphore_p (nsync_semaphore *s) { return err; } -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ +/* Like nsync_mu_semaphore_p() this waits for the count of *s to exceed 0, + while additionally supporting a time parameter specifying at what point + in the future ETIMEDOUT should be returned, if neither cancellation, or + semaphore release happens. */ errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) { errno_t err; BEGIN_CANCELLATION_POINT; diff --git a/third_party/nsync/mu_semaphore_futex.c b/third_party/nsync/mu_semaphore_futex.c index ef8017c47..d5fba7d4e 100644 --- a/third_party/nsync/mu_semaphore_futex.c +++ b/third_party/nsync/mu_semaphore_futex.c @@ -49,7 +49,10 @@ void nsync_mu_semaphore_init_futex (nsync_semaphore *s) { f->i = 0; } -/* Wait until the count of *s exceeds 0, and decrement it. */ +/* Wait until the count of *s exceeds 0, and decrement it. If POSIX cancellations + are currently disabled by the thread, then this function always succeeds. When + they're enabled in MASKED mode, this function may return ECANCELED. Otherwise, + cancellation will occur by unwinding cleanup handlers pushed to the stack. */ errno_t nsync_mu_semaphore_p_futex (nsync_semaphore *s) { struct futex *f = (struct futex *) s; int i; @@ -74,9 +77,10 @@ errno_t nsync_mu_semaphore_p_futex (nsync_semaphore *s) { return result; } -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ +/* Like nsync_mu_semaphore_p() this waits for the count of *s to exceed 0, + while additionally supporting a time parameter specifying at what point + in the future ETIMEDOUT should be returned, if neither cancellation, or + semaphore release happens. */ errno_t nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time abs_deadline) { struct futex *f = (struct futex *)s; int i; diff --git a/third_party/nsync/mu_semaphore_gcd.c b/third_party/nsync/mu_semaphore_gcd.c index a2a1e1dba..7bf36e395 100644 --- a/third_party/nsync/mu_semaphore_gcd.c +++ b/third_party/nsync/mu_semaphore_gcd.c @@ -16,11 +16,15 @@ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/sig.internal.h" #include "libc/errno.h" #include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" #include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" +#include "libc/thread/tls.h" #include "third_party/nsync/atomic.h" #include "third_party/nsync/atomic.internal.h" #include "third_party/nsync/futex.internal.h" @@ -36,33 +40,48 @@ static dispatch_semaphore_t dispatch_semaphore_create(long count) { dispatch_semaphore_t ds; - ds = __syslib->dispatch_semaphore_create (count); + ds = __syslib->__dispatch_semaphore_create (count); STRACE ("dispatch_semaphore_create(%ld) → %#lx", count, ds); return (ds); } static void dispatch_release (dispatch_semaphore_t ds) { - __syslib->dispatch_release (ds); + __syslib->__dispatch_release (ds); STRACE ("dispatch_release(%#lx)", ds); } static long dispatch_semaphore_wait (dispatch_semaphore_t ds, dispatch_time_t dt) { - long rc = __syslib->dispatch_semaphore_wait (ds, dt); + long rc = __syslib->__dispatch_semaphore_wait (ds, dt); STRACE ("dispatch_semaphore_wait(%#lx, %ld) → %ld", ds, dt, rc); return (rc); } static long dispatch_semaphore_signal (dispatch_semaphore_t ds) { - long rc = __syslib->dispatch_semaphore_signal (ds); + long rc = __syslib->__dispatch_semaphore_signal (ds); (void)rc; STRACE ("dispatch_semaphore_signal(%#lx) → %ld", ds, rc); return (ds); } static dispatch_time_t dispatch_walltime (const struct timespec *base, - int64_t offset) { - return __syslib->dispatch_walltime (base, offset); + int64_t offset) { + return __syslib->__dispatch_walltime (base, offset); +} + +static errno_t nsync_dispatch_semaphore_wait (nsync_semaphore *s, + nsync_time abs_deadline) { + errno_t result = 0; + dispatch_time_t dt; + if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) { + dt = DISPATCH_TIME_FOREVER; + } else { + dt = dispatch_walltime (&abs_deadline, 0); + } + if (dispatch_semaphore_wait (*(dispatch_semaphore_t *)s, dt) != 0) { + result = ETIMEDOUT; + } + return (result); } /* Initialize *s; the initial value is 0. */ @@ -75,30 +94,47 @@ void nsync_mu_semaphore_destroy_gcd (nsync_semaphore *s) { dispatch_release (*(dispatch_semaphore_t *)s); } -/* Wait until the count of *s exceeds 0, and decrement it. */ +/* Wait until the count of *s exceeds 0, and decrement it. If POSIX cancellations + are currently disabled by the thread, then this function always succeeds. When + they're enabled in MASKED mode, this function may return ECANCELED. Otherwise, + cancellation will occur by unwinding cleanup handlers pushed to the stack. */ errno_t nsync_mu_semaphore_p_gcd (nsync_semaphore *s) { - dispatch_semaphore_wait (*(dispatch_semaphore_t *)s, - DISPATCH_TIME_FOREVER); - return (0); + return nsync_mu_semaphore_p_with_deadline_gcd (s, nsync_time_no_deadline); } -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ +/* Like nsync_mu_semaphore_p() this waits for the count of *s to exceed 0, + while additionally supporting a time parameter specifying at what point + in the future ETIMEDOUT should be returned, if neither cancellation, or + semaphore release happens. */ errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s, nsync_time abs_deadline) { errno_t result = 0; - if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) { - dispatch_semaphore_wait (*(dispatch_semaphore_t *)s, - DISPATCH_TIME_FOREVER); + struct PosixThread *pt; + if (!__tls_enabled || + !_weaken (pthread_testcancel_np) || + !(pt = _pthread_self()) || + (pt->pt_flags & PT_NOCANCEL)) { + nsync_dispatch_semaphore_wait (s, abs_deadline); } else { - struct timespec ts; - bzero (&ts, sizeof (ts)); - ts.tv_sec = NSYNC_TIME_SEC (abs_deadline); - ts.tv_nsec = NSYNC_TIME_NSEC (abs_deadline); - if (dispatch_semaphore_wait (*(dispatch_semaphore_t *)s, - dispatch_walltime (&abs_deadline, 0)) != 0) { - result = ETIMEDOUT; + struct timespec now, until, slice; + slice = timespec_frommillis (__SIG_LOCK_INTERVAL_MS); + for (;;) { + if (_weaken (pthread_testcancel_np) () == ECANCELED) { + result = ECANCELED; + break; + } + now = timespec_real(); + if (timespec_cmp (now, abs_deadline) >= 0) { + result = ETIMEDOUT; + break; + } + until = timespec_add (now, slice); + if (timespec_cmp (until, abs_deadline) > 0) { + until = abs_deadline; + } + if (!nsync_dispatch_semaphore_wait (s, until)) { + break; + } } } return (result); diff --git a/third_party/nsync/mu_semaphore_sem.c b/third_party/nsync/mu_semaphore_sem.c index cc6027a26..0f9061c9b 100644 --- a/third_party/nsync/mu_semaphore_sem.c +++ b/third_party/nsync/mu_semaphore_sem.c @@ -17,15 +17,19 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/cp.internal.h" #include "libc/calls/struct/timespec.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/cosmo.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" +#include "libc/thread/thread.h" #include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/time.h" // clang-format off @@ -35,21 +39,51 @@ */ #define ASSERT(x) npassert(x) +#define SEM_CONTAINER(e) DLL_CONTAINER(struct sem, list, e) struct sem { int64_t id; + struct Dll list; }; +static struct { + atomic_uint once; + pthread_spinlock_t lock; + struct Dll *list; +} g_sems; + static nsync_semaphore *sem_big_enough_for_sem = (nsync_semaphore *) (uintptr_t)(1 / (sizeof (struct sem) <= sizeof (*sem_big_enough_for_sem))); +static void nsync_mu_semaphore_sem_fork_child (void) { + struct Dll *e; + for (e = dll_first (g_sems.list); e; e = dll_next (g_sems.list, e)) { + sys_close (SEM_CONTAINER (e)->id); + } + g_sems.list = 0; /* list memory is on dead thread stacks */ + (void) pthread_spin_init (&g_sems.lock, 0); +} + +static void nsync_mu_semaphore_sem_init (void) { + pthread_atfork (0, 0, nsync_mu_semaphore_sem_fork_child); +} + /* Initialize *s; the initial value is 0. */ void nsync_mu_semaphore_init_sem (nsync_semaphore *s) { + int lol; struct sem *f = (struct sem *) s; f->id = 0; ASSERT (!sys_sem_init (0, &f->id)); + if ((lol = __sys_fcntl (f->id, F_DUPFD_CLOEXEC, 50)) >= 50) { + sys_close (f->id); + f->id = lol; + } + cosmo_once (&g_sems.once, nsync_mu_semaphore_sem_init); + pthread_spin_lock (&g_sems.lock); + dll_init (&f->list); + dll_make_first (&g_sems.list, &f->list); + pthread_spin_unlock (&g_sems.lock); STRACE ("sem_init(0, [%ld]) → 0", f->id); - ASSERT (__sys_fcntl (f->id, F_SETFD, FD_CLOEXEC) == 0); // ouch } /* Releases system resources associated with *s. */ @@ -57,10 +91,16 @@ void nsync_mu_semaphore_destroy_sem (nsync_semaphore *s) { int rc; struct sem *f = (struct sem *) s; ASSERT (!(rc = sys_sem_destroy (f->id))); - STRACE ("sem_destroy(%ld) → %d", rc); + pthread_spin_lock (&g_sems.lock); + dll_remove (&g_sems.list, &f->list); + pthread_spin_unlock (&g_sems.lock); + STRACE ("sem_destroy(%ld) → %d", f->id, rc); } -/* Wait until the count of *s exceeds 0, and decrement it. */ +/* Wait until the count of *s exceeds 0, and decrement it. If POSIX cancellations + are currently disabled by the thread, then this function always succeeds. When + they're enabled in MASKED mode, this function may return ECANCELED. Otherwise, + cancellation will occur by unwinding cleanup handlers pushed to the stack. */ errno_t nsync_mu_semaphore_p_sem (nsync_semaphore *s) { int e, rc; errno_t result; @@ -78,9 +118,10 @@ errno_t nsync_mu_semaphore_p_sem (nsync_semaphore *s) { return result; } -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ +/* Like nsync_mu_semaphore_p() this waits for the count of *s to exceed 0, + while additionally supporting a time parameter specifying at what point + in the future ETIMEDOUT should be returned, if neither cancellation, or + semaphore release happens. */ errno_t nsync_mu_semaphore_p_with_deadline_sem (nsync_semaphore *s, nsync_time abs_deadline) { int e, rc; errno_t result; diff --git a/third_party/nsync/nsync.mk b/third_party/nsync/nsync.mk index ccb17bff2..1a04e163d 100644 --- a/third_party/nsync/nsync.mk +++ b/third_party/nsync/nsync.mk @@ -49,9 +49,13 @@ $(THIRD_PARTY_NSYNC_A).pkg: \ $(foreach x,$(THIRD_PARTY_NSYNC_A_DIRECTDEPS),$($(x)_A).pkg) $(THIRD_PARTY_NSYNC_A_OBJS): private \ - CCFLAGS += \ + COPTS += \ + -ffreestanding \ + -fdata-sections \ -ffunction-sections \ - -fdata-sections + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 # these assembly files are safe to build on aarch64 o/$(MODE)/third_party/nsync/compat.o: third_party/nsync/compat.S diff --git a/third_party/nsync/testing/mu_starvation_test.c b/third_party/nsync/testing/mu_starvation_test.c index 15ed904d6..3ee6bebf9 100644 --- a/third_party/nsync/testing/mu_starvation_test.c +++ b/third_party/nsync/testing/mu_starvation_test.c @@ -17,6 +17,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/str/str.h" +#include "libc/thread/thread.h" #include "third_party/nsync/mu.h" #include "third_party/nsync/mu_wait.h" #include "third_party/nsync/testing/closure.h" @@ -137,7 +138,7 @@ static void test_starve_with_readers (testing t) { trylock_acquires++; nsync_mu_unlock (&sd.mu); } - sched_yield (); + pthread_yield (); } if (trylock_acquires != 0) { TEST_ERROR (t, ("expected no acquisitions via nsync_mu_trylock(), got %d\n", @@ -256,7 +257,7 @@ static void test_starve_with_writer (testing t) { trylock_acquires++; nsync_mu_unlock (&sd.mu); } - sched_yield (); + pthread_yield (); } if (trylock_acquires < expected_lo) { TEST_ERROR (t, ("expected at least %d acquisitions via " @@ -276,7 +277,7 @@ static void test_starve_with_writer (testing t) { rtrylock_acquires++; nsync_mu_runlock (&sd.mu); } - sched_yield (); + pthread_yield (); } if (rtrylock_acquires < expected_lo) { TEST_ERROR (t, ("expected at least %d acquisitions via " diff --git a/third_party/nsync/testing/mu_test.c b/third_party/nsync/testing/mu_test.c index bd2150d60..c1f1942d1 100644 --- a/third_party/nsync/testing/mu_test.c +++ b/third_party/nsync/testing/mu_test.c @@ -227,7 +227,7 @@ static void counting_loop_try_mu (test_data *td, int id) { int n = td->loop_count; for (i = 0; i != n; i++) { while (!nsync_mu_trylock (&td->mu)) { - sched_yield (); + pthread_yield (); } td->id = id; td->i++; diff --git a/third_party/nsync/waiter_per_thread.c b/third_party/nsync/waiter_per_thread.c deleted file mode 100644 index 3ac2772f1..000000000 --- a/third_party/nsync/waiter_per_thread.c +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "libc/thread/thread.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -static pthread_key_t waiter_key; -static nsync_atomic_uint32_ pt_once; - -static void do_once (nsync_atomic_uint32_ *ponce, void (*dest) (void *)) { - uint32_t o = ATM_LOAD_ACQ (ponce); - if (o != 2) { - while (o == 0 && !ATM_CAS_ACQ (ponce, 0, 1)) { - o = ATM_LOAD (ponce); - } - if (o == 0) { - pthread_key_create (&waiter_key, dest); - ATM_STORE_REL (ponce, 2); - } - while (ATM_LOAD_ACQ (ponce) != 2) { - nsync_yield_ (); - } - } -} - -void *nsync_per_thread_waiter_ (void (*dest) (void *)) { - do_once (&pt_once, dest); - return (pthread_getspecific (waiter_key)); -} - -void nsync_set_per_thread_waiter_ (void *v, void (*dest) (void *)) { - do_once (&pt_once, dest); - pthread_setspecific (waiter_key, v); -} diff --git a/third_party/nsync/yield.c b/third_party/nsync/yield.c index bf7081c66..2eac400b2 100644 --- a/third_party/nsync/yield.c +++ b/third_party/nsync/yield.c @@ -18,10 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/intrin/strace.internal.h" +#include "libc/thread/thread.h" #include "third_party/nsync/common.internal.h" // clang-format off void nsync_yield_ (void) { - sched_yield (); + pthread_yield (); STRACE ("nsync_yield_()"); } diff --git a/third_party/puff/puff.mk b/third_party/puff/puff.mk index 0fd58c8a3..3777b2012 100644 --- a/third_party/puff/puff.mk +++ b/third_party/puff/puff.mk @@ -33,6 +33,13 @@ $(THIRD_PARTY_PUFF_A).pkg: \ $(THIRD_PARTY_PUFF_A_OBJS) \ $(foreach x,$(THIRD_PARTY_PUFF_A_DIRECTDEPS),$($(x)_A).pkg) +$(THIRD_PARTY_PUFF_A_OBJS): private \ + COPTS += \ + -ffreestanding \ + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + THIRD_PARTY_PUFF_LIBS = $(foreach x,$(THIRD_PARTY_PUFF_ARTIFACTS),$($(x))) THIRD_PARTY_PUFF_SRCS = $(foreach x,$(THIRD_PARTY_PUFF_ARTIFACTS),$($(x)_SRCS)) THIRD_PARTY_PUFF_HDRS = $(foreach x,$(THIRD_PARTY_PUFF_ARTIFACTS),$($(x)_HDRS)) diff --git a/third_party/python/Python/sysmodule.c b/third_party/python/Python/sysmodule.c index 855df1efc..dccb6f0a9 100644 --- a/third_party/python/Python/sysmodule.c +++ b/third_party/python/Python/sysmodule.c @@ -2419,7 +2419,7 @@ sys_pyfile_write(const char *text, PyObject *file) */ static void -sys_write(_Py_Identifier *key, FILE *fp, const char *format, va_list va) +sys_write_(_Py_Identifier *key, FILE *fp, const char *format, va_list va) { PyObject *file; PyObject *error_type, *error_value, *error_traceback; @@ -2447,7 +2447,7 @@ PySys_WriteStdout(const char *format, ...) va_list va; va_start(va, format); - sys_write(&PyId_stdout, stdout, format, va); + sys_write_(&PyId_stdout, stdout, format, va); va_end(va); } @@ -2457,7 +2457,7 @@ PySys_WriteStderr(const char *format, ...) va_list va; va_start(va, format); - sys_write(&PyId_stderr, stderr, format, va); + sys_write_(&PyId_stderr, stderr, format, va); va_end(va); } diff --git a/third_party/python/pyobj.c b/third_party/python/pyobj.c index 1084ace26..26aca6d11 100644 --- a/third_party/python/pyobj.c +++ b/third_party/python/pyobj.c @@ -678,9 +678,6 @@ Objectify(void) } else if (insertlauncher) { elfwriter_yoink(elf, "LaunchPythonModule", STB_GLOBAL); } - if (isunittest) { - elfwriter_yoink(elf, "testlib_quota_handlers", STB_GLOBAL); - } elfwriter_finishsection(elf); if (insertrunner || insertlauncher) { n = strlen(modname) + 1; diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 32c94a956..2912be9d9 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -1167,6 +1167,7 @@ THIRD_PARTY_PYTHON_STAGE2_A_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_THREAD \ LIBC_SOCK \ @@ -2155,7 +2156,7 @@ o/$(MODE)/third_party/python/Lib/test/test_timeout.py.runs: \ PYTHONTESTER = o/$(MODE)/third_party/python/pythontester.com o/$(MODE)/third_party/python/Lib/test/test_grammar.py.runs: $(PYTHONTESTER) - $(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_grammar $(PYTESTARGS) + @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_grammar $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_set.py.runs: $(PYTHONTESTER) @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_set $(PYTESTARGS) diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 76f489606..f5e38aaab 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -85,6 +85,7 @@ THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index ed9b90e51..72e5092f4 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -48,6 +48,7 @@ THIRD_PARTY_SQLITE3_A_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ diff --git a/third_party/xed/xed.mk b/third_party/xed/xed.mk index 4339a57b0..f70b2cfc3 100644 --- a/third_party/xed/xed.mk +++ b/third_party/xed/xed.mk @@ -50,6 +50,13 @@ o/$(MODE)/third_party/xed/x86ild.greg.o: private \ CFLAGS += \ -O3 +$(THIRD_PARTY_XED_A_OBJS): private \ + COPTS += \ + -ffreestanding \ + -fno-sanitize=address \ + -Wframe-larger-than=4096 \ + -Walloca-larger-than=4096 + HIRD_PARTY_XED_LIBS = $(foreach x,$(THIRD_PARTY_XED_ARTIFACTS),$($(x))) THIRD_PARTY_XED_SRCS = $(foreach x,$(THIRD_PARTY_XED_ARTIFACTS),$($(x)_SRCS)) THIRD_PARTY_XED_HDRS = $(foreach x,$(THIRD_PARTY_XED_ARTIFACTS),$($(x)_HDRS)) diff --git a/third_party/zip/zip.mk b/third_party/zip/zip.mk index 761563b6f..1dad1605b 100644 --- a/third_party/zip/zip.mk +++ b/third_party/zip/zip.mk @@ -87,6 +87,7 @@ THIRD_PARTY_ZIP_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ LIBC_STDIO \ + LIBC_PROC \ LIBC_STR \ LIBC_SYSV \ LIBC_TIME \ diff --git a/tool/build/build.mk b/tool/build/build.mk index 207488fb9..ada3f0b98 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -36,6 +36,7 @@ TOOL_BUILD_DIRECTDEPS = \ LIBC_NT_KERNEL32 \ LIBC_NT_USER32 \ LIBC_NT_WS2_32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/tool/build/compile.c b/tool/build/compile.c index c05fe7b4f..e7fc21e1c 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -49,7 +49,7 @@ #include "libc/nexgen32e/x86info.h" #include "libc/runtime/runtime.h" #include "libc/stdio/append.h" -#include "libc/stdio/posix_spawn.h" +#include "libc/proc/posix_spawn.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/clock.h" @@ -811,7 +811,7 @@ char *MakeTmpOut(const char *path) { char *p = tmpout; char *e = tmpout + sizeof(tmpout) - 1; g_tmpout_original = path; - p = stpcpy(p, kTmpPath); + p = stpcpy(p, __get_tmpdir()); while ((c = *path++)) { if (c == '/') c = '_'; if (p == e) { diff --git a/tool/build/deltaify.c b/tool/build/deltaify.c deleted file mode 100644 index 4121e4a47..000000000 --- a/tool/build/deltaify.c +++ /dev/null @@ -1,104 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/struct/sigaction.h" -#include "libc/calls/struct/siginfo.h" -#include "libc/calls/ucontext.h" -#include "libc/errno.h" -#include "libc/log/check.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/sig.h" -#include "libc/time/time.h" -#include "libc/x/xgetline.h" - -static int pid; - -static void RelaySig(int sig, struct siginfo *si, void *uc) { - kill(pid, sig); -} - -int main(int argc, char *argv[]) { - FILE *f; - bool ok; - char *s, *p; - int64_t micros; - long double t1, t2; - int ws, pipefds[2]; - setvbuf(stdout, malloc(BUFSIZ), _IOLBF, BUFSIZ); - setvbuf(stderr, malloc(BUFSIZ), _IOLBF, BUFSIZ); - if (argc < 2) { - f = stdin; - t1 = nowl(); - } else { - sigset_t block, mask; - struct sigaction saignore = {.sa_handler = SIG_IGN}; - struct sigaction sadefault = {.sa_handler = SIG_DFL}; - struct sigaction sarelay = {.sa_sigaction = RelaySig, - .sa_flags = SA_RESTART}; - sigemptyset(&block); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGQUIT); - sigaddset(&mask, SIGCHLD); - sigprocmask(SIG_BLOCK, &block, &mask); - sigaction(SIGHUP, &sarelay, 0); - sigaction(SIGTERM, &sarelay, 0); - sigaction(SIGINT, &saignore, 0); - sigaction(SIGQUIT, &saignore, 0); - CHECK_NE(-1, pipe(pipefds)); - CHECK_NE(-1, (pid = vfork())); - if (!pid) { - close(pipefds[0]); - dup2(pipefds[1], 1); - sigaction(SIGHUP, &sadefault, NULL); - sigaction(SIGTERM, &sadefault, NULL); - sigaction(SIGINT, &sadefault, NULL); - sigaction(SIGQUIT, &sadefault, NULL); - sigprocmask(SIG_SETMASK, &mask, NULL); - execvp(argv[1], argv + 1); - _exit(127); - } - t1 = nowl(); - close(pipefds[1]); - sigprocmask(SIG_SETMASK, &mask, NULL); - CHECK((f = fdopen(pipefds[0], "r"))); - CHECK_NE(-1, setvbuf(f, malloc(4096), _IOLBF, 4096)); - } - if ((p = xgetline(f))) { - t2 = nowl(); - micros = (t2 - t1) * 1e6; - t1 = t2; - printf("%16ld\n", micros); - do { - s = xgetline(f); - t2 = nowl(); - micros = (t2 - t1) * 1e6; - t1 = t2; - printf("%16ld %s", micros, p); - free(p); - } while ((p = s)); - } - ok = !ferror(f); - if (argc < 2) return ok ? 0 : 1; - fclose(f); - while (waitpid(pid, &ws, 0) == -1) CHECK_EQ(EINTR, errno); - return !WIFEXITED(ws) ? 128 + WTERMSIG(ws) : ok ? WEXITSTATUS(ws) : 1; -} diff --git a/tool/build/elf2pe.c b/tool/build/elf2pe.c index ae4aa95f1..f640e926f 100644 --- a/tool/build/elf2pe.c +++ b/tool/build/elf2pe.c @@ -23,6 +23,7 @@ #include "libc/elf/scalar.h" #include "libc/elf/struct/rela.h" #include "libc/elf/struct/shdr.h" +#include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/bits.h" #include "libc/intrin/describeflags.internal.h" @@ -56,18 +57,20 @@ "copyright 2023 justine tunney\n" \ "https://github.com/jart/cosmopolitan\n" -#define MANUAL \ - " -o OUTPUT INPUT\n" \ - "\n" \ - "DESCRIPTION\n" \ - "\n" \ - " Converts ELF executables to PE\n" \ - "\n" \ - "FLAGS\n" \ - "\n" \ - " -h show usage\n" \ - " -o OUTPUT set output path\n" \ - " -D PATH embed dos/bios stub\n" \ +#define MANUAL \ + " -o OUTPUT INPUT\n" \ + "\n" \ + "DESCRIPTION\n" \ + "\n" \ + " Converts ELF executables to PE\n" \ + "\n" \ + "FLAGS\n" \ + "\n" \ + " -h show usage\n" \ + " -o OUTPUT set output path\n" \ + " -D PATH embed dos/bios stub\n" \ + " -S SIZE size of stack commit\n" \ + " -R SIZE size of stack reserve\n" \ "\n" #define MAX_ALIGN 65536 @@ -152,6 +155,8 @@ struct Elf { static const char *prog; static const char *outpath; static const char *stubpath; +static long FLAG_SizeOfStackCommit = 64 * 1024; +static long FLAG_SizeOfStackReserve = 8 * 1024 * 1024; static wontreturn void Die(const char *thing, const char *reason) { tinyprint(2, thing, ": ", reason, "\n", NULL); @@ -846,8 +851,9 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) { opthdr->Subsystem = kNtImageSubsystemWindowsCui; opthdr->DllCharacteristics = kNtImageDllcharacteristicsNxCompat | kNtImageDllcharacteristicsHighEntropyVa; - opthdr->SizeOfStackReserve = 8 * 1024 * 1024; - opthdr->SizeOfStackCommit = 64 * 1024; + opthdr->SizeOfStackReserve = + MAX(FLAG_SizeOfStackReserve, FLAG_SizeOfStackCommit); + opthdr->SizeOfStackCommit = FLAG_SizeOfStackCommit; // output data directory entries if (elf->imports) { @@ -1033,7 +1039,7 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) { static void GetOpts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "ho:D:")) != -1) { + while ((opt = getopt(argc, argv, "ho:D:R:S:")) != -1) { switch (opt) { case 'o': outpath = optarg; @@ -1041,6 +1047,12 @@ static void GetOpts(int argc, char *argv[]) { case 'D': stubpath = optarg; break; + case 'S': + FLAG_SizeOfStackCommit = sizetol(optarg, 1024); + break; + case 'R': + FLAG_SizeOfStackReserve = sizetol(optarg, 1024); + break; case 'h': ShowUsage(0, 1); default: diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index 4285cdbd2..60e56c788 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -44,6 +44,7 @@ TOOL_BUILD_LIB_A_DIRECTDEPS = \ LIBC_STR \ LIBC_SYSV \ LIBC_SYSV_CALLS \ + LIBC_PROC \ LIBC_THREAD \ LIBC_TIME \ LIBC_TINYMATH \ diff --git a/tool/build/lib/eztls.c b/tool/build/lib/eztls.c index b2f6547e1..ab51908ec 100644 --- a/tool/build/lib/eztls.c +++ b/tool/build/lib/eztls.c @@ -24,8 +24,8 @@ #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" -#include "libc/runtime/syslib.internal.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/thread.h" #include "libc/x/x.h" #include "libc/x/xsigaction.h" #include "net/https/https.h" diff --git a/tool/build/runit.c b/tool/build/runit.c index 465ad4b7e..e75e03823 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" #include "libc/dns/dns.h" #include "libc/errno.h" #include "libc/fmt/conv.h" @@ -55,6 +56,8 @@ #include "libc/x/xasprintf.h" #include "libc/x/xsigaction.h" #include "net/https/https.h" +#include "third_party/mbedtls/debug.h" +#include "third_party/mbedtls/net_sockets.h" #include "third_party/mbedtls/ssl.h" #include "third_party/zlib/zlib.h" #include "tool/build/lib/eztls.h" @@ -147,8 +150,8 @@ void CheckExists(const char *path) { void Connect(void) { const char *ip4; int rc, err, expo; - long double t1, t2; struct addrinfo *ai; + struct timespec deadline; if ((rc = getaddrinfo(g_hostname, _gc(xasprintf("%hu", g_runitdport)), &kResolvHints, &ai)) != 0) { FATALF("%s:%hu: EAI_%s %m", g_hostname, g_runitdport, gai_strerror(rc)); @@ -166,7 +169,8 @@ void Connect(void) { CHECK_NE(-1, (g_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))); expo = INITIAL_CONNECT_TIMEOUT; - t1 = nowl(); + deadline = timespec_add(timespec_real(), + timespec_fromseconds(MAX_WAIT_CONNECT_SECONDS)); LOGIFNEG1(sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, 0)); DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0], ip4[1], ip4[2], ip4[3], g_prog); @@ -178,11 +182,10 @@ TryAgain: NULL)); rc = connect(g_sock, ai->ai_addr, ai->ai_addrlen); err = errno; - t2 = nowl(); if (rc == -1) { if (err == EINTR) { expo *= 1.5; - if (t2 > t1 + MAX_WAIT_CONNECT_SECONDS) { + if (timespec_cmp(timespec_real(), deadline) >= 0) { FATALF("timeout connecting to %s (%hhu.%hhu.%hhu.%hhu:%d)", g_hostname, ip4[0], ip4[1], ip4[2], ip4[3], ntohs(ai->ai_addr4->sin_port)); __builtin_unreachable(); @@ -293,10 +296,17 @@ bool Recv(char *p, int n) { do rc = mbedtls_ssl_read(&ezssl, p + i, n - i); while (rc == MBEDTLS_ERR_SSL_WANT_READ); if (!rc) return false; - if (rc < 0) EzTlsDie("read response failed", rc); + if (rc < 0) { + if (rc == MBEDTLS_ERR_NET_CONN_RESET) { + EzTlsDie("connection reset", rc); + } else { + EzTlsDie("read response failed", rc); + } + } } return true; } + int ReadResponse(void) { int exitcode; for (;;) { @@ -319,7 +329,7 @@ int ReadResponse(void) { exitcode = 202; break; } - exitcode = *msg; + exitcode = *msg & 255; if (exitcode) { WARNF("%s says %s exited with %d", g_hostname, g_prog, exitcode); } else { @@ -355,6 +365,7 @@ int RunOnHost(char *spec) { sscanf(spec, "%100s %hu %hu", g_hostname, &g_runitdport, &g_sshport); if (got < 1) { kprintf("what on earth %#s -> %d\n", spec, got); + fprintf(stderr, "what on earth %#s -> %d\n", spec, got); exit(1); } if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test."); @@ -466,6 +477,7 @@ int SpawnSubprocesses(int argc, char *argv[]) { int main(int argc, char *argv[]) { ShowCrashReports(); signal(SIGPIPE, SIG_IGN); + // mbedtls_debug_threshold = 3; if (getenv("DEBUG")) { __log_level = kLogDebug; } diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 83b53e68e..da37e5b4e 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -39,13 +39,13 @@ #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" +#include "libc/proc/posix_spawn.h" #include "libc/runtime/runtime.h" #include "libc/runtime/syslib.internal.h" #include "libc/sock/sock.h" #include "libc/sock/struct/pollfd.h" #include "libc/sock/struct/sockaddr.h" #include "libc/stdio/append.h" -#include "libc/stdio/posix_spawn.h" #include "libc/stdio/rand.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -249,8 +249,8 @@ void StartTcpServer(void) { uint32_t asize; g_servfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); if (g_servfd == -1) { - fprintf(stderr, program_invocation_short_name, - ": socket failed: ", strerror(errno), "\n", NULL); + fprintf(stderr, "%s: socket failed: %s\n", program_invocation_short_name, + strerror(errno)); exit(1); } struct timeval timeo = {DEATH_CLOCK_SECONDS / 10}; @@ -259,8 +259,8 @@ void StartTcpServer(void) { setsockopt(g_servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); if (bind(g_servfd, (struct sockaddr *)&g_servaddr, sizeof(g_servaddr)) == -1) { - fprintf(stderr, program_invocation_short_name, - ": bind failed: ", strerror(errno), "\n", NULL); + fprintf(stderr, "%s: bind failed: %s\n", program_invocation_short_name, + strerror(errno)); exit(1); } unassert(!listen(g_servfd, 10)); @@ -474,7 +474,7 @@ void *ClientWorker(void *arg) { crc = READ32BE(msg + 13); origname = gc(calloc(1, namesize + 1)); Recv(client, origname, namesize); - INFOF("%s sent %#s (%'u bytes @ %#s)", addrstr, origname, filesize, + VERBF("%s sent %#s (%'u bytes @ %#s)", addrstr, origname, filesize, client->tmpexepath); char *exedata = gc(malloc(filesize)); Recv(client, exedata, filesize); @@ -490,14 +490,23 @@ void *ClientWorker(void *arg) { // thus we use an optimistic approach to avoid expensive locks sprintf(client->tmpexepath, "o/%s.XXXXXX.com", basename(origname)); int exefd = openatemp(AT_FDCWD, client->tmpexepath, 4, O_CLOEXEC, 0700); - ftruncate(exefd, filesize); + if (exefd == -1) { + WARNF("%s failed to open temporary file %#s due to %m", addrstr, + client->tmpexepath); + pthread_exit(0); + } + if (ftruncate(exefd, filesize)) { + WARNF("%s failed to write %#s due to %m", addrstr, origname); + close(exefd); + pthread_exit(0); + } if (write(exefd, exedata, filesize) != filesize) { - WARNF("%s failed to write %#s", addrstr, origname); + WARNF("%s failed to write %#s due to %m", addrstr, origname); close(exefd); pthread_exit(0); } if (close(exefd)) { - WARNF("%s failed to close %#s", addrstr, origname); + WARNF("%s failed to close %#s due to %m", addrstr, origname); pthread_exit(0); } @@ -534,9 +543,11 @@ RetryOnEtxtbsyRaceCondition: } } errno_t err; + struct timespec started; posix_spawnattr_t spawnattr; posix_spawn_file_actions_t spawnfila; sigemptyset(&sigmask); + started = timespec_real(); pipe2(client->pipe, O_CLOEXEC); posix_spawnattr_init(&spawnattr); posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_SETPGROUP); @@ -591,8 +602,8 @@ RetryOnEtxtbsyRaceCondition: INFOF("poll interrupted"); continue; } else { - WARNF("killing %d %s and hanging up %d because poll failed", client->fd, - origname, client->pid); + WARNF("killing %d %s and hanging up %d because poll failed with %m", + client->fd, origname, client->pid); goto HangupClientAndTerminateJob; } } @@ -616,6 +627,10 @@ RetryOnEtxtbsyRaceCondition: client->pid); continue; } + if (received == MBEDTLS_ERR_SSL_CANCELED) { // EAGAIN SO_RCVTIMEO + WARNF("%s (pid %d) is is canceling job", origname, client->pid); + goto HangupClientAndTerminateJob; + } WARNF("client ssl read failed with -0x%04x (%s) so killing %s", -received, GetTlsError(received), origname); goto TerminateJob; @@ -647,21 +662,28 @@ WaitAgain: kill(client->pid, SIGINT); goto WaitAgain; } + if (errno == ECANCELED) { + WARNF("thread is canceled; killing %s pid %d", origname, client->pid); + kill(client->pid, SIGKILL); + goto WaitAgain; + } WARNF("waitpid failed %m"); client->pid = 0; goto HangupClientAndTerminateJob; } client->pid = 0; int exitcode; + struct timespec ended = timespec_real(); + int64_t micros = timespec_tomicros(timespec_sub(ended, started)); if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus)) { - WARNF("%s on %s exited with %d", origname, g_hostname, - WEXITSTATUS(wstatus)); - appendf(&client->output, "------ %s %s $?=%d (0x%08x) ------\n", - g_hostname, origname, WEXITSTATUS(wstatus), wstatus); + WARNF("%s on %s exited with $?=%d after %'ldµs", origname, g_hostname, + WEXITSTATUS(wstatus), micros); + appendf(&client->output, "------ %s %s $?=%d (0x%08x) %,ldµs ------\n", + g_hostname, origname, WEXITSTATUS(wstatus), wstatus, micros); } else { - VERBF("%s on %s exited with %d", origname, g_hostname, - WEXITSTATUS(wstatus)); + INFOF("%s on %s exited with $?=%d after %'ldµs", origname, g_hostname, + WEXITSTATUS(wstatus), micros); } exitcode = WEXITSTATUS(wstatus); } else if (WIFSIGNALED(wstatus)) { @@ -670,14 +692,16 @@ WaitAgain: client->output = 0; goto RetryOnEtxtbsyRaceCondition; } - WARNF("%s on %s terminated with %s", origname, g_hostname, - strsignal(WTERMSIG(wstatus))); + char sigbuf[21]; + WARNF("%s on %s terminated after %'ldµs with %s", origname, g_hostname, + micros, strsignal_r(WTERMSIG(wstatus), sigbuf)); exitcode = 128 + WTERMSIG(wstatus); - appendf(&client->output, "------ %s %s $?=%s (0x%08x) ------\n", g_hostname, - origname, strsignal(WTERMSIG(wstatus)), wstatus); + appendf(&client->output, "------ %s %s $?=%s (0x%08x) %,ldµs ------\n", + g_hostname, origname, strsignal(WTERMSIG(wstatus)), wstatus, + micros); } else { - WARNF("%s on %s died with wait status 0x%08x", origname, g_hostname, - wstatus); + WARNF("%s on %s died after %'ldµs with wait status 0x%08x", origname, + g_hostname, micros, wstatus); exitcode = 127; } if (wstatus) { @@ -701,6 +725,7 @@ void HandleClient(void) { client->addrsize = sizeof(client->addr); for (;;) { if (g_interrupted) { + pthread_cancel(0); free(client); return; } @@ -765,7 +790,7 @@ void Daemonize(void) { } int main(int argc, char *argv[]) { -#if IsModeDbg() +#ifndef NDEBUG ShowCrashReports(); #endif GetOpts(argc, argv); diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index b6f8a0907..b9cac3fc9 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -211,6 +211,7 @@ void zipobj(int argc, char **argv) { } int main(int argc, char **argv) { + ShowCrashReports(); timestamp.tv_sec = 1647414000; /* determinism */ /* clock_gettime(CLOCK_REALTIME, ×tamp); */ zipobj(argc, argv); diff --git a/tool/hello/hello.mk b/tool/hello/hello.mk index 3f676ac8c..a02891122 100644 --- a/tool/hello/hello.mk +++ b/tool/hello/hello.mk @@ -76,4 +76,22 @@ o/$(MODE)/tool/hello/hello-pe.com: \ o/$(MODE)/tool/build/elf2pe.com @$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe.com -o $@ $< +# elf2pe can generate binaries that don't have dll imports +o/$(MODE)/tool/hello/life-pe.com.dbg: \ + o/$(MODE)/tool/hello/life-pe.o + @$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain +o/$(MODE)/tool/hello/life-pe.com: \ + o/$(MODE)/tool/hello/life-pe.com.dbg \ + o/$(MODE)/tool/build/elf2pe.com + @$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe.com -o $@ $< + +# demonstrates in process monitor the lowest resource usage a win32 app can have +o/$(MODE)/tool/hello/wait-pe.com.dbg: \ + o/$(MODE)/tool/hello/wait-pe.o + @$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain +o/$(MODE)/tool/hello/wait-pe.com: \ + o/$(MODE)/tool/hello/wait-pe.com.dbg \ + o/$(MODE)/tool/build/elf2pe.com + @$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe.com -R 64kb -S 4kb -o $@ $< + $(TOOL_HELLO_OBJS): tool/hello/hello.mk diff --git a/tool/hello/life-pe.c b/tool/hello/life-pe.c new file mode 100644 index 000000000..6f6098d1d --- /dev/null +++ b/tool/hello/life-pe.c @@ -0,0 +1,13 @@ +#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 + +__attribute__((__ms_abi__)) long WinMain(void) { + return 42 << 8; +} diff --git a/tool/hello/wait-pe.c b/tool/hello/wait-pe.c new file mode 100644 index 000000000..154d17b19 --- /dev/null +++ b/tool/hello/wait-pe.c @@ -0,0 +1,19 @@ +#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 "tool/build/elf2pe.h" + +#define STD_OUTPUT_HANDLE -11u + +__dll_import("kernel32.dll", uint32_t, SleepEx, (uint32_t, bool32)); + +__attribute__((__ms_abi__)) long WinMain(void) { + SleepEx(-1, true); + return 0; +} diff --git a/tool/net/definitions.lua b/tool/net/definitions.lua index 230ef1695..bbe4476af 100644 --- a/tool/net/definitions.lua +++ b/tool/net/definitions.lua @@ -7266,8 +7266,7 @@ function unix.tiocgwinsz(fd) end --- --- This creates a secure temporary file inside `$TMPDIR`. If it isn't --- defined, then `/tmp` is used on UNIX and GetTempPath() is used on ---- the New Technology. This resolution of `$TMPDIR` happens once in a ---- ctor, which is copied to the `kTmpPath` global. +--- the New Technology. This resolution of `$TMPDIR` happens once. --- --- Once close() is called, the returned file is guaranteed to be --- deleted automatically. On UNIX the file is unlink()'d before this diff --git a/tool/net/help.txt b/tool/net/help.txt index 432a410db..27581c36f 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -4710,8 +4710,7 @@ UNIX MODULE This creates a secure temporary file inside `$TMPDIR`. If it isn't defined, then `/tmp` is used on UNIX and GetTempPath() is used on - the New Technology. This resolution of `$TMPDIR` happens once in a - ctor, which is copied to the `kTmpPath` global. + the New Technology. This resolution of `$TMPDIR` happens once. Once close() is called, the returned file is guaranteed to be deleted automatically. On UNIX the file is unlink()'d before this diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 4470dc76f..829b5928e 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -21,6 +21,7 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" #include "libc/dns/dns.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" @@ -51,6 +52,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sock.h" +#include "libc/thread/thread.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "net/http/escape.h" @@ -106,7 +108,8 @@ int LuaBin(lua_State *L) { } int LuaGetTime(lua_State *L) { - lua_pushnumber(L, nowl()); + struct timespec now = timespec_real(); + lua_pushnumber(L, now.tv_sec + now.tv_nsec * 1e-9); return 1; } @@ -894,7 +897,7 @@ int LuaBenchmark(lua_State *L) { for (attempts = 0;;) { lua_gc(L, LUA_GCCOLLECT); - sched_yield(); + pthread_yield(); core = TSC_AUX_CORE(Rdpid()); interrupts = GetInterrupts(); for (avgticks = iter = 1; iter < count; ++iter) { @@ -916,7 +919,7 @@ int LuaBenchmark(lua_State *L) { for (attempts = 0;;) { lua_gc(L, LUA_GCCOLLECT); - sched_yield(); + pthread_yield(); core = TSC_AUX_CORE(Rdpid()); interrupts = GetInterrupts(); for (avgticks = iter = 1; iter < count; ++iter) { @@ -937,7 +940,7 @@ int LuaBenchmark(lua_State *L) { avgticks = MAX(avgticks - overhead, 0); lua_gc(L, LUA_GCRESTART); - lua_pushinteger(L, ConvertTicksToNanos(round(avgticks))); + lua_pushinteger(L, avgticks / 3); lua_pushinteger(L, round(avgticks)); lua_pushinteger(L, round(overhead)); lua_pushinteger(L, attempts); diff --git a/tool/net/net.mk b/tool/net/net.mk index 1a89ecb1d..fd16ca27e 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -20,8 +20,7 @@ TOOL_NET_COMS = \ o/$(MODE)/tool/net/redbean-demo.com \ o/$(MODE)/tool/net/redbean-static.com \ o/$(MODE)/tool/net/redbean-unsecure.com \ - o/$(MODE)/tool/net/redbean-original.com \ - o/$(MODE)/tool/net/wb.com + o/$(MODE)/tool/net/redbean-original.com TOOL_NET_CHECKS = \ o/$(MODE)/tool/net/net.pkg \ @@ -38,6 +37,7 @@ TOOL_NET_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_NT_IPHLPAPI \ LIBC_NT_KERNEL32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 5e8f89f43..f6d5c0c63 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -54,12 +54,10 @@ #include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" -#include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/vendor.internal.h" #include "libc/nexgen32e/x86feature.h" #include "libc/nt/enum/fileflagandattributes.h" -#include "libc/nt/thread.h" #include "libc/runtime/clktck.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" @@ -102,7 +100,6 @@ #include "libc/sysv/consts/timer.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/spawn.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" #include "libc/x/x.h" @@ -151,12 +148,6 @@ __static_yoink("blink_linux_aarch64"); // for raspberry pi __static_yoink("blink_xnu_aarch64"); // is apple silicon #endif -#if !IsTiny() -#ifdef __x86_64__ -__static_yoink("ShowCrashReportsEarly"); -#endif -#endif - /** * @fileoverview redbean - single-file distributable web server * @@ -514,8 +505,8 @@ static struct Strings hidepaths; static const char *launchbrowser; static const char ctIdx = 'c'; // a pseudo variable to get address of -static struct spawn replth; -static struct spawn monitorth; +static pthread_t replth; +static pthread_t monitorth; static struct Buffer inbuf_actual; static struct Buffer inbuf; static struct Buffer oldin; @@ -6549,7 +6540,10 @@ static int ExitWorker(void) { } if (monitortty) { terminatemonitor = true; - _join(&monitorth); + if (monitorth) { + pthread_join(monitorth, 0); + monitorth = 0; + } } _Exit(0); } @@ -6582,7 +6576,7 @@ static int EnableSandbox(void) { } } -static int MemoryMonitor(void *arg, int tid) { +static void *MemoryMonitor(void *arg) { static struct termios oldterm; static int tty; sigset_t ss; @@ -6738,8 +6732,9 @@ static int MemoryMonitor(void *arg, int tid) { } static void MonitorMemory(void) { - if (_spawn(MemoryMonitor, 0, &monitorth) == -1) { - WARNF("(memv) failed to start memory monitor %m"); + errno_t err; + if ((err = pthread_create(&monitorth, 0, MemoryMonitor, 0))) { + WARNF("(memv) failed to start memory monitor %s", strerror(err)); } } @@ -7443,10 +7438,16 @@ void RedBean(int argc, char *argv[]) { if (!isexitingworker) { if (!IsTiny()) { terminatemonitor = true; - _join(&monitorth); + if (monitorth) { + pthread_join(monitorth, 0); + monitorth = 0; + } } #ifndef STATIC - _join(&replth); + if (replth) { + pthread_join(replth, 0); + replth = 0; + } #endif HandleShutdown(); CallSimpleHookIfDefined("OnServerStop"); @@ -7459,7 +7460,7 @@ void RedBean(int argc, char *argv[]) { } int main(int argc, char *argv[]) { -#if !IsTiny() && !defined(__x86_64__) +#if !IsTiny() ShowCrashReports(); #endif LoadZipArgs(&argc, &argv); @@ -7470,7 +7471,10 @@ int main(int argc, char *argv[]) { // 2. unwound worker exit if (IsModeDbg()) { if (isexitingworker) { - _join(&replth); + if (replth) { + pthread_join(replth, 0); + replth = 0; + } linenoiseDisableRawMode(); linenoiseHistoryFree(); } diff --git a/tool/net/wb.c b/tool/net/wb.c deleted file mode 100644 index 99f3114d4..000000000 --- a/tool/net/wb.c +++ /dev/null @@ -1,506 +0,0 @@ -/*-*- 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 │ -│ │ -│ 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/dns/dns.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/log/check.h" -#include "libc/log/log.h" -#include "libc/macros.internal.h" -#include "libc/math.h" -#include "libc/mem/gc.h" -#include "libc/mem/mem.h" -#include "libc/sock/goodsocket.internal.h" -#include "libc/sock/sock.h" -#include "libc/stdio/append.h" -#include "libc/stdio/rand.h" -#include "libc/stdio/stdio.h" -#include "libc/str/slice.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/ex.h" -#include "libc/sysv/consts/exit.h" -#include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/so.h" -#include "libc/sysv/consts/sock.h" -#include "libc/sysv/consts/sol.h" -#include "libc/sysv/consts/tcp.h" -#include "libc/time/time.h" -#include "libc/x/x.h" -#include "libc/x/xsigaction.h" -#include "net/http/http.h" -#include "net/http/url.h" -#include "net/https/https.h" -#include "third_party/getopt/getopt.internal.h" -#include "third_party/mbedtls/ctr_drbg.h" -#include "third_party/mbedtls/debug.h" -#include "third_party/mbedtls/error.h" -#include "third_party/mbedtls/net_sockets.h" -#include "third_party/mbedtls/ssl.h" - -#define OPTS "BIqksvzX:H:C:m:" - -#define Micros(t) ((int64_t)((t)*1e6)) -#define HasHeader(H) (!!msg.headers[H].a) -#define HeaderData(H) (inbuf.p + msg.headers[H].a) -#define HeaderLength(H) (msg.headers[H].b - msg.headers[H].a) -#define HeaderEqualCase(H, S) \ - SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) - -struct Buffer { - size_t n, c; - char *p; -}; - -struct Headers { - size_t n; - char **p; -} headers; - -bool suiteb; -char *request; -bool isdone; -char *urlarg; -int method = kHttpGet; -bool authmode = MBEDTLS_SSL_VERIFY_NONE; - -char *host; -char *port; -char *flags; -bool usessl; -uint32_t ip; -struct Url url; -struct addrinfo *addr; -struct Buffer inbuf; - -long error_count; -long failure_count; -long message_count; -long connect_count; -double *latencies; -size_t latencies_n; -size_t latencies_c; -long double start_run; -long double end_run; -long double start_fetch; -long double end_fetch; -long connectionstobemade = 100; -long messagesperconnection = 100; - -mbedtls_x509_crt *cachain; -mbedtls_ssl_config conf; -mbedtls_ssl_context ssl; -mbedtls_ctr_drbg_context drbg; - -struct addrinfo hints = {.ai_family = AF_INET, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - .ai_flags = AI_NUMERICSERV}; - -void OnInt(int sig) { - isdone = true; -} - -static int TlsSend(void *c, const unsigned char *p, size_t n) { - int rc; - if ((rc = write(*(int *)c, p, n)) == -1) { - if (errno == EINTR) { - return MBEDTLS_ERR_SSL_WANT_WRITE; - } else if (errno == EAGAIN) { - return MBEDTLS_ERR_SSL_TIMEOUT; - } else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) { - return MBEDTLS_ERR_NET_CONN_RESET; - } else { - VERBOSEF("tls write() error %s", strerror(errno)); - return MBEDTLS_ERR_NET_RECV_FAILED; - } - } - return rc; -} - -static int TlsRecv(void *c, unsigned char *p, size_t n, uint32_t o) { - int r; - if ((r = read(*(int *)c, p, n)) == -1) { - if (errno == EINTR) { - return MBEDTLS_ERR_SSL_WANT_READ; - } else if (errno == EAGAIN) { - return MBEDTLS_ERR_SSL_TIMEOUT; - } else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) { - return MBEDTLS_ERR_NET_CONN_RESET; - } else { - VERBOSEF("tls read() error %s", strerror(errno)); - return MBEDTLS_ERR_NET_RECV_FAILED; - } - } - return r; -} - -static wontreturn void PrintUsage(FILE *f, int rc) { - fprintf(f, "usage: %s [-%s] URL\n", OPTS, program_invocation_name); - fprintf(f, "wb - cosmopolitan http/https benchmark tool\n"); - fprintf(f, " -C INT connections to be made\n"); - fprintf(f, " -m INT messages per connection\n"); - fprintf(f, " -B use suite b ciphersuites\n"); - fprintf(f, " -v increase verbosity\n"); - fprintf(f, " -H K:V append http header\n"); - fprintf(f, " -X NAME specify http method\n"); - fprintf(f, " -k verify ssl certs\n"); - fprintf(f, " -I same as -X HEAD\n"); - fprintf(f, " -z same as -H Accept-Encoding:gzip\n"); - fprintf(f, " -h show this help\n"); - exit(rc); -} - -int fetch(void) { - int status; - ssize_t rc; - int t, ret, sock; - long messagesremaining; - struct HttpMessage msg; - struct HttpUnchunker u; - size_t g, n, hdrsize, paylen; - - messagesremaining = messagesperconnection; - - /* - * Setup crypto. - */ - if (usessl) { - mbedtls_ssl_session_reset(&ssl); - CHECK_EQ(0, mbedtls_ssl_set_hostname(&ssl, host)); - } - - /* - * Connect to server. - */ - InitHttpMessage(&msg, kHttpResponse); - ip = ntohl(((struct sockaddr_in *)addr->ai_addr)->sin_addr.s_addr); - CHECK_NE(-1, (sock = GoodSocket(addr->ai_family, addr->ai_socktype, - addr->ai_protocol, false, 0))); - if (connect(sock, addr->ai_addr, addr->ai_addrlen) == -1) { - goto TransportError; - } - if (usessl) { - mbedtls_ssl_set_bio(&ssl, &sock, TlsSend, 0, TlsRecv); - if ((ret = mbedtls_ssl_handshake(&ssl))) { - goto TransportError; - } - } - -SendAnother: - - /* - * Send HTTP Message. - */ - n = appendz(request).i; - if (usessl) { - ret = mbedtls_ssl_write(&ssl, request, n); - if (ret != n) goto TransportError; - } else if (write(sock, request, n) != n) { - goto TransportError; - } - - /* - * Handle response. - */ - InitHttpMessage(&msg, kHttpResponse); - for (hdrsize = paylen = t = 0;;) { - if (inbuf.n == inbuf.c) { - inbuf.c += 1000; - inbuf.c += inbuf.c >> 1; - inbuf.p = realloc(inbuf.p, inbuf.c); - } - if (usessl) { - if ((rc = mbedtls_ssl_read(&ssl, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) < - 0) { - if (rc == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - rc = 0; - } else { - goto TransportError; - } - } - } else if ((rc = read(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) { - goto TransportError; - } - g = rc; - inbuf.n += g; - switch (t) { - case kHttpClientStateHeaders: - if (!g) goto TransportError; - rc = ParseHttpMessage(&msg, inbuf.p, inbuf.n); - if (rc == -1) goto TransportError; - if (rc) { - hdrsize = rc; - if (100 <= msg.status && msg.status <= 199) { - if ((HasHeader(kHttpContentLength) && - !HeaderEqualCase(kHttpContentLength, "0")) || - (HasHeader(kHttpTransferEncoding) && - !HeaderEqualCase(kHttpTransferEncoding, "identity"))) { - goto TransportError; - } - DestroyHttpMessage(&msg); - InitHttpMessage(&msg, kHttpResponse); - memmove(inbuf.p, inbuf.p + hdrsize, inbuf.n - hdrsize); - inbuf.n -= hdrsize; - break; - } - if (msg.status == 204 || msg.status == 304) { - goto Finished; - } - if (HasHeader(kHttpTransferEncoding) && - !HeaderEqualCase(kHttpTransferEncoding, "identity")) { - if (HeaderEqualCase(kHttpTransferEncoding, "chunked")) { - t = kHttpClientStateBodyChunked; - bzero(&u, sizeof(u)); - goto Chunked; - } else { - goto TransportError; - } - } else if (HasHeader(kHttpContentLength)) { - rc = ParseContentLength(HeaderData(kHttpContentLength), - HeaderLength(kHttpContentLength)); - if (rc == -1) goto TransportError; - if ((paylen = rc) <= inbuf.n - hdrsize) { - goto Finished; - } else { - t = kHttpClientStateBodyLengthed; - } - } else { - t = kHttpClientStateBody; - } - } - break; - case kHttpClientStateBody: - if (!g) { - paylen = inbuf.n; - goto Finished; - } - break; - case kHttpClientStateBodyLengthed: - if (!g) goto TransportError; - if (inbuf.n - hdrsize >= paylen) goto Finished; - break; - case kHttpClientStateBodyChunked: - Chunked: - rc = Unchunk(&u, inbuf.p + hdrsize, inbuf.n - hdrsize, &paylen); - if (rc == -1) goto TransportError; - if (rc) goto Finished; - break; - default: - __builtin_unreachable(); - } - } - -Finished: - status = msg.status; - DestroyHttpMessage(&msg); - if (!isdone && status == 200 && --messagesremaining > 0) { - long double now = nowl(); - end_fetch = now; - ++message_count; - latencies = realloc(latencies, ++latencies_n * sizeof(*latencies)); - latencies[latencies_n - 1] = end_fetch - start_fetch; - start_fetch = now; - goto SendAnother; - } - close(sock); - return status; -TransportError: - close(sock); - DestroyHttpMessage(&msg); - return 900; -} - -int main(int argc, char *argv[]) { - xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); - xsigaction(SIGINT, OnInt, 0, 0, 0); - - /* - * Read flags. - */ - int opt; - __log_level = kLogWarn; - while ((opt = getopt(argc, argv, OPTS)) != -1) { - switch (opt) { - case 's': - case 'q': - break; - case 'B': - suiteb = true; - appendf(&flags, " -B"); - break; - case 'v': - ++__log_level; - break; - case 'I': - method = kHttpHead; - appendf(&flags, " -I"); - break; - case 'H': - headers.p = realloc(headers.p, ++headers.n * sizeof(*headers.p)); - headers.p[headers.n - 1] = optarg; - appendf(&flags, " -H '%s'", optarg); - break; - case 'z': - headers.p = realloc(headers.p, ++headers.n * sizeof(*headers.p)); - headers.p[headers.n - 1] = "Accept-Encoding: gzip"; - appendf(&flags, " -z"); - break; - case 'X': - CHECK((method = GetHttpMethod(optarg, strlen(optarg)))); - appendf(&flags, " -X %s", optarg); - break; - case 'k': - authmode = MBEDTLS_SSL_VERIFY_REQUIRED; - appendf(&flags, " -k"); - break; - case 'm': - messagesperconnection = strtol(optarg, 0, 0); - break; - case 'C': - connectionstobemade = strtol(optarg, 0, 0); - break; - case 'h': - PrintUsage(stdout, EXIT_SUCCESS); - default: - PrintUsage(stderr, EX_USAGE); - } - } - - appendf(&flags, " -m %ld", messagesperconnection); - appendf(&flags, " -C %ld", connectionstobemade); - - if (optind == argc) PrintUsage(stdout, EXIT_SUCCESS); - urlarg = argv[optind]; - cachain = GetSslRoots(); - - long connectsremaining = connectionstobemade; - - /* - * Parse URL. - */ - _gc(ParseUrl(urlarg, -1, &url, kUrlPlus)); - _gc(url.params.p); - usessl = false; - if (url.scheme.n) { - if (url.scheme.n == 5 && !memcasecmp(url.scheme.p, "https", 5)) { - usessl = true; - } else if (!(url.scheme.n == 4 && !memcasecmp(url.scheme.p, "http", 4))) { - FATALF("bad scheme"); - } - } - if (url.host.n) { - host = _gc(strndup(url.host.p, url.host.n)); - if (url.port.n) { - port = _gc(strndup(url.port.p, url.port.n)); - } else { - port = usessl ? "443" : "80"; - } - } else { - host = "127.0.0.1"; - port = "80"; - } - CHECK(IsAcceptableHost(host, -1)); - url.fragment.p = 0, url.fragment.n = 0; - url.scheme.p = 0, url.scheme.n = 0; - url.user.p = 0, url.user.n = 0; - url.pass.p = 0, url.pass.n = 0; - url.host.p = 0, url.host.n = 0; - url.port.p = 0, url.port.n = 0; - if (!url.path.n || url.path.p[0] != '/') { - char *p = _gc(xmalloc(1 + url.path.n)); - mempcpy(mempcpy(p, "/", 1), url.path.p, url.path.n); - url.path.p = p; - ++url.path.n; - } - - /* - * Create HTTP message. - */ - appendf(&request, - "%s %s HTTP/1.1\r\n" - "Host: %s:%s\r\n", - kHttpMethod[method], _gc(EncodeUrl(&url, 0)), host, port); - for (int i = 0; i < headers.n; ++i) { - appendf(&request, "%s\r\n", headers.p[i]); - } - appendf(&request, "\r\n"); - - /* - * Perform DNS lookup. - */ - int rc; - if ((rc = getaddrinfo(host, port, &hints, &addr)) != EAI_SUCCESS) { - FATALF("getaddrinfo(%s:%s) failed", host, port); - } - - /* - * Setup SSL crypto. - */ - mbedtls_ssl_init(&ssl); - mbedtls_ctr_drbg_init(&drbg); - mbedtls_ssl_config_init(&conf); - CHECK_EQ(0, mbedtls_ctr_drbg_seed(&drbg, GetEntropy, 0, "justine", 7)); - CHECK_EQ(0, - mbedtls_ssl_config_defaults( - &conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, - suiteb ? MBEDTLS_SSL_PRESET_SUITEB : MBEDTLS_SSL_PRESET_SUITEC)); - mbedtls_ssl_conf_authmode(&conf, authmode); - mbedtls_ssl_conf_ca_chain(&conf, cachain, 0); - mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &drbg); - CHECK_EQ(0, mbedtls_ssl_setup(&ssl, &conf)); - - int status; - latencies_c = 1024; - latencies = malloc(latencies_c * sizeof(*latencies)); - start_run = nowl(); - while (!isdone && --connectsremaining >= 0) { - start_fetch = nowl(); - status = fetch(); - end_fetch = nowl(); - if (status == 200) { - ++connect_count; - ++message_count; - latencies = realloc(latencies, ++latencies_n * sizeof(*latencies)); - latencies[latencies_n - 1] = end_fetch - start_fetch; - } else if (status == 900) { - ++failure_count; - } else { - ++error_count; - } - } - end_run = nowl(); - - double latencies_sum = fsum(latencies, latencies_n); - double avg_latency = latencies_sum / message_count; - - printf("wb%s\n", flags); - printf("msgs / second: %,ld qps\n", - (int64_t)(message_count / (end_run - start_run))); - printf("run time: %,ldµs\n", Micros(end_run - start_run)); - printf("latency / msgs: %,ldµs\n", Micros(avg_latency)); - printf("message count: %,ld\n", message_count); - printf("connect count: %,ld\n", connect_count); - printf("error count: %,ld (non-200 responses)\n", error_count); - printf("failure count: %,ld (transport error)\n", failure_count); - - return 0; -} diff --git a/tool/viz/lib/ycbcr2rgb3.c b/tool/viz/lib/ycbcr2rgb3.c index 37354299d..0c09c461a 100644 --- a/tool/viz/lib/ycbcr2rgb3.c +++ b/tool/viz/lib/ycbcr2rgb3.c @@ -25,8 +25,10 @@ #include "dsp/core/illumination.h" #include "dsp/core/q.h" #include "dsp/scale/scale.h" +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/timespec.h" #include "libc/intrin/bsr.h" #include "libc/intrin/pmulhrsw.h" #include "libc/log/check.h" @@ -64,7 +66,7 @@ const double kSrgbToXyz[3][3] = { long magikarp_latency_; long gyarados_latency_; long ycbcr2rgb_latency_; -long double magikarp_start_; +struct timespec magikarp_start_; struct YCbCr { bool yonly; @@ -261,15 +263,14 @@ void YCbCrConvert(struct YCbCr *me, long yn, long xn, const unsigned char Y[restrict yys][yxs], long cys, long cxs, unsigned char Cb[restrict cys][cxs], unsigned char Cr[restrict cys][cxs]) { - long double ts; - ts = nowl(); + struct timespec ts = timespec_real(); if (!me->yonly) { YCbCr2Rgb(yn, xn, RGB, yys, yxs, Y, cys, cxs, Cb, Cr, me->magnums, me->lighting, me->transfer[pf10_]); } else { Y2Rgb(yn, xn, RGB, yys, yxs, Y, me->magnums, me->transfer[pf10_]); } - ycbcr2rgb_latency_ = lroundl((nowl() - ts) * 1e6l); + ycbcr2rgb_latency_ = timespec_tomicros(timespec_sub(timespec_real(), ts)); } void YCbCr2RgbScaler(struct YCbCr *me, long dyn, long dxn, @@ -279,7 +280,6 @@ void YCbCr2RgbScaler(struct YCbCr *me, long dyn, long dxn, unsigned char Cr[restrict cys][cxs], long yyn, long yxn, long cyn, long cxn, double syn, double sxn, double pry, double prx) { - long double ts; long scyn, scxn; double yry, yrx, cry, crx, yoy, yox, coy, cox; scyn = syn * cyn / yyn; @@ -297,8 +297,8 @@ void YCbCr2RgbScaler(struct YCbCr *me, long dyn, long dxn, Magkern2xY(cys, cxs, Cr, scyn, scxn), HALF(yyn), yxn, HALF(cyn), scxn, syn / 2, sxn, pry, prx); } else { - magikarp_latency_ = lroundl((nowl() - magikarp_start_) * 1e6l); - ts = nowl(); + struct timespec ts = timespec_real(); + magikarp_latency_ = timespec_tomicros(timespec_sub(ts, magikarp_start_)); yry = syn / dyn; yrx = sxn / dxn; cry = syn * cyn / yyn / dyn; @@ -326,7 +326,7 @@ void YCbCr2RgbScaler(struct YCbCr *me, long dyn, long dxn, me->chroma.cy, me->chroma.cx, false); GyaradosUint8(cys, cxs, Cr, cys, cxs, Cr, dyn, dxn, scyn, scxn, 0, 255, me->chroma.cy, me->chroma.cx, false); - gyarados_latency_ = lround((nowl() - ts) * 1e6l); + gyarados_latency_ = timespec_tomicros(timespec_sub(timespec_real(), ts)); YCbCrConvert(me, dyn, dxn, RGB, yys, yxs, Y, cys, cxs, Cb, Cr); INFOF("done"); } @@ -381,7 +381,7 @@ void *YCbCr2RgbScale(long dyn, long dxn, CHECK_LE(cyn, cys); CHECK_LE(cxn, cxs); INFOF("magikarp2x"); - magikarp_start_ = nowl(); + magikarp_start_ = timespec_real(); minyys = MAX(ceil(syn), MAX(yyn, ceil(dyn * pry))); minyxs = MAX(ceil(sxn), MAX(yxn, ceil(dxn * prx))); mincys = MAX(cyn, ceil(dyn * pry)); diff --git a/tool/viz/life.c b/tool/viz/life.c index fd39d9a54..cad2edbdb 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -1094,13 +1094,12 @@ static bool HasPendingInput(void) { } static bool ShouldDraw(void) { - long double now, rate; - static long double next; + struct timespec now; + static struct timespec next; if (!isdragging) return true; - now = nowl(); - rate = 1. / 24; - if (now > next && !HasPendingInput()) { - next = now + rate; + now = timespec_real(); + if (timespec_cmp(now, next) > 0 && !HasPendingInput()) { + next = timespec_add(now, timespec_frommicros(1. / 24 * 1e6)); return true; } else { return false; diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 3c38efd09..e2f25ff23 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -331,12 +331,12 @@ static long Index(long y, long x) { } static void PreventBufferbloat(void) { - long double now, rate; - static long double last; - now = nowl(); - rate = 1. / fps; - if (now - last < rate) { - dsleep(rate - (now - last)); + struct timespec now, rate; + static struct timespec last; + now = timespec_real(); + rate = timespec_frommicros(1. / fps * 1e6); + if (timespec_cmp(timespec_sub(now, last), rate) < 0) { + timespec_sleep(timespec_sub(rate, timespec_sub(now, last))); } last = now; } diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index b8f2e273c..3d57e83dd 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -33,6 +33,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/winsize.h" #include "libc/calls/termios.h" #include "libc/calls/ucontext.h" @@ -42,6 +43,7 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/xchg.internal.h" #include "libc/log/check.h" @@ -88,6 +90,7 @@ #include "libc/sysv/consts/termios.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" #include "libc/time/time.h" #include "libc/x/xsigaction.h" #include "third_party/getopt/getopt.internal.h" @@ -174,11 +177,11 @@ mode.\n\ *(B) = pvalloc(N); \ }) -#define TIMEIT(OUT_NANOS, FORM) \ - do { \ - long double Start = nowl(); \ - FORM; \ - (OUT_NANOS) = (uint64_t)((nowl() - Start) * 1e9L); \ +#define TIMEIT(OUT_NANOS, FORM) \ + do { \ + struct timespec Start = timespec_real(); \ + FORM; \ + (OUT_NANOS) = timespec_tonanos(timespec_sub(timespec_real(), Start)); \ } while (0) typedef bool (*openspeaker_f)(void); @@ -278,9 +281,9 @@ static uint64_t t1, t2, t3, t4, t5, t6, t8; static const char *sox_, *ffplay_, *patharg_; static struct VtFrame vtframe_[2], *f1_, *f2_; static struct Graphic graphic_[2], *g1_, *g2_; -static long double deadline_, dura_, starttime_; +static struct timespec deadline_, dura_, starttime_; static bool yes_, stats_, dither_, ttymode_, istango_; -static long double decode_start_, f1_start_, f2_start_; +static struct timespec decode_start_, f1_start_, f2_start_; static int16_t pcm_[PLM_AUDIO_SAMPLES_PER_FRAME * 2 / 8][8]; static int16_t pcmscale_[PLM_AUDIO_SAMPLES_PER_FRAME * 2 / 8][8]; static bool fullclear_, historyclear_, tuned_, yonly_, gotvideo_; @@ -308,23 +311,18 @@ static void StrikeDownCrapware(int sig) { kill(playpid_, SIGKILL); } -static long AsMilliseconds(long double ts) { - return rintl(ts * 1e3); -} - -static long AsNanoseconds(long double ts) { - return rintl(ts * 1e9); -} - -static long double GetGraceTime(void) { - return deadline_ - nowl(); +static struct timespec GetGraceTime(void) { + return timespec_sub(deadline_, timespec_real()); } static int GetNamedVector(const struct NamedVector *choices, size_t n, const char *s) { int i; char name[sizeof(choices->name)]; - strlcpy(name, s, sizeof(name)); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wstringop-truncation" + strncpy(name, s, sizeof(name)); +#pragma GCC pop_options strntoupper(name, sizeof(name)); for (i = 0; i < n; ++i) { if (memcmp(choices[i].name, name, sizeof(name)) == 0) { @@ -345,7 +343,7 @@ static int GetLighting(const char *s) { static bool CloseSpeaker(void) { int rc, wstatus; rc = 0; - sched_yield(); + pthread_yield(); if (playfd_) { rc |= close(playfd_); playfd_ = -1; @@ -375,8 +373,13 @@ static void ResizeVtFrame(struct VtFrame *f, size_t yn, size_t xn) { f->i = f->n = 0; } +static float timespec_tofloat(struct timespec ts) { + return ts.tv_sec + ts.tv_nsec * 1e-9; +} + static void RecordFactThatFrameWasFullyRendered(void) { - fcring_.p[fcring_.i] = nowl() - starttime_; + fcring_.p[fcring_.i] = + timespec_tofloat(timespec_sub(timespec_real(), starttime_)); fcring_.n += 1; fcring_.i += 1; fcring_.i &= ARRAYLEN(fcring_.p) - 1; @@ -521,7 +524,7 @@ static bool OpenSpeaker(void) { if (ffplay_) tryspeakerfns_[i++] = TryFfplay; if (sox_) tryspeakerfns_[i++] = TrySox; } - snprintf(fifopath_, sizeof(fifopath_), "%s%s.%d.%d.wav", kTmpPath, + snprintf(fifopath_, sizeof(fifopath_), "%s%s.%d.%d.wav", __get_tmpdir(), firstnonnull(program_invocation_short_name, "unknown"), getpid(), count); for (i = 0; i < ARRAYLEN(tryspeakerfns_); ++i) { @@ -539,7 +542,7 @@ static bool OpenSpeaker(void) { static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) { if (playfd_ != -1) { - DEBUGF("OnAudio() [grace=%,ldns]", AsNanoseconds(GetGraceTime())); + DEBUGF("OnAudio() [grace=%,ldns]", timespec_tonanos(GetGraceTime())); CHECK_EQ(2, chans_); CHECK_EQ(ARRAYLEN(pcm_) * 8, samples->count * chans_); float2short(ARRAYLEN(pcm_), pcm_, (void *)samples->interleaved); @@ -549,7 +552,7 @@ static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) { TryAgain: if (WriteAudio(playfd_, pcm_, sizeof(pcm_), 1000) != -1) { DEBUGF("WriteAudio(%d, %zu) ok [grace=%,ldns]", playfd_, - samples->count * 2, AsNanoseconds(GetGraceTime())); + samples->count * 2, timespec_tonanos(GetGraceTime())); } else { WARNF("WriteAudio(%d, %zu) failed: %s", playfd_, samples->count * 2, strerror(errno)); @@ -765,7 +768,7 @@ static void RasterIt(void) { static void TranscodeVideo(plm_frame_t *pf) { CHECK_EQ(pf->cb.width, pf->cr.width); CHECK_EQ(pf->cb.height, pf->cr.height); - DEBUGF("TranscodeVideo() [grace=%,ldns]", AsNanoseconds(GetGraceTime())); + DEBUGF("TranscodeVideo() [grace=%,ldns]", timespec_tonanos(GetGraceTime())); g2_ = &graphic_[1]; t5 = 0; @@ -879,8 +882,9 @@ static ssize_t WriteVideoCall(void) { if (plm_get_audio_enabled(plm_)) { plm_set_audio_lead_time( plm_, - MAX(0, MIN(nowl() - f1_start_, plm_get_samplerate(plm_) / - PLM_AUDIO_SAMPLES_PER_FRAME))); + max(0, + min(timespec_tofloat(timespec_sub(timespec_real(), f1_start_)), + plm_get_samplerate(plm_) / PLM_AUDIO_SAMPLES_PER_FRAME))); } f1_start_ = f2_start_; f1_->i = f1_->n = 0; @@ -904,10 +908,10 @@ static void DrainVideo(void) { static void WriteVideo(void) { ssize_t rc; - DEBUGF("write(tty) grace=%,ldns", AsNanoseconds(GetGraceTime())); + DEBUGF("write(tty) grace=%,ldns", timespec_tonanos(GetGraceTime())); if ((rc = WriteVideoCall()) != -1) { DEBUGF("write(tty) → %zd [grace=%,ldns]", rc, - AsNanoseconds(GetGraceTime())); + timespec_tonanos(GetGraceTime())); } else if (errno == EAGAIN || errno == EINTR) { DEBUGF("write(tty) → EINTR"); longjmp(jbi_, 1); @@ -1269,11 +1273,11 @@ static void PerformBestEffortIo(void) { {infd_, POLLIN}, {outfd_, f1_ && f1_->n ? POLLOUT : 0}, }; - pollms = MAX(0, AsMilliseconds(GetGraceTime())); + pollms = MAX(0, timespec_tomillis(GetGraceTime())); DEBUGF("poll() ms=%,d", pollms); if ((toto = poll(fds, ARRAYLEN(fds), pollms)) != -1) { DEBUGF("poll() toto=%d [grace=%,ldns]", toto, - AsNanoseconds(GetGraceTime())); + timespec_tonanos(GetGraceTime())); if (toto) { if (fds[0].revents & (POLLIN | POLLERR)) ReadKeyboard(); if (fds[1].revents & (POLLOUT | POLLERR)) WriteVideo(); @@ -1305,32 +1309,36 @@ static void HandleSignals(void) { } static void PrintVideo(void) { - long double decode_last, decode_end, next_tick, lag; - dura_ = MIN(MAX_FRAMERATE, 1 / plm_get_framerate(plm_)); + struct timespec decode_last, decode_end, next_tick, lag; + dura_ = timespec_frommicros(min(MAX_FRAMERATE, 1 / plm_get_framerate(plm_)) * + 1e6); INFOF("framerate=%f dura=%f", plm_get_framerate(plm_), dura_); - next_tick = deadline_ = decode_last = nowl(); - next_tick += dura_; - deadline_ += dura_; + next_tick = deadline_ = decode_last = timespec_real(); + next_tick = timespec_add(next_tick, dura_); + deadline_ = timespec_add(deadline_, dura_); do { - DEBUGF("plm_decode [grace=%,ldns]", AsNanoseconds(GetGraceTime())); - decode_start_ = nowl(); - plm_decode(plm_, decode_start_ - decode_last); + DEBUGF("plm_decode [grace=%,ldns]", timespec_tonanos(GetGraceTime())); + decode_start_ = timespec_real(); + plm_decode(plm_, + timespec_tofloat(timespec_sub(decode_start_, decode_last))); decode_last = decode_start_; - decode_end = nowl(); - lag = decode_end - decode_start_; - while (decode_end + lag > next_tick) next_tick += dura_; - deadline_ = next_tick - lag; + decode_end = timespec_real(); + lag = timespec_sub(decode_end, decode_start_); + while (timespec_cmp(timespec_add(decode_end, lag), next_tick) > 0) { + next_tick = timespec_add(next_tick, dura_); + } + deadline_ = timespec_sub(next_tick, lag); if (gotvideo_ || !plm_get_video_enabled(plm_)) { gotvideo_ = false; INFOF("entering printvideo event loop (lag=%,ldns, grace=%,ldns)", - AsNanoseconds(lag), AsNanoseconds(GetGraceTime())); + timespec_tonanos(lag), timespec_tonanos(GetGraceTime())); } do { if (!setjmp(jbi_)) { PerformBestEffortIo(); } HandleSignals(); - } while (AsMilliseconds(GetGraceTime()) > 0); + } while (timespec_tomillis(GetGraceTime()) > 0); } while (plm_ && !plm_has_ended(plm_)); } @@ -1366,7 +1374,7 @@ static void PrintUsage(int rc, FILE *f) { static void GetOpts(int argc, char *argv[]) { int opt; - snprintf(logpath_, sizeof(logpath_), "%s%s.log", kTmpPath, + snprintf(logpath_, sizeof(logpath_), "%s%s.log", __get_tmpdir(), firstnonnull(program_invocation_short_name, "unknown")); while ((opt = getopt(argc, argv, "?34AGSTVYabdfhnpstxyzvL:")) != -1) { switch (opt) { @@ -1533,6 +1541,7 @@ static void TryToOpenFrameBuffer(void) { int main(int argc, char *argv[]) { sigset_t wut; const char *s; + ShowCrashReports(); gamma_ = 2.4; volscale_ -= 2; dither_ = true; @@ -1581,7 +1590,7 @@ int main(int argc, char *argv[]) { if (t2 > t1) longjmp(jb_, 1); OpenVideo(); DimensionDisplay(); - starttime_ = nowl(); + starttime_ = timespec_real(); PrintVideo(); } INFOF("jb_ triggered"); diff --git a/tool/viz/tailf.c b/tool/viz/tailf.c index 78f755f80..76d2056f0 100644 --- a/tool/viz/tailf.c +++ b/tool/viz/tailf.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) { } chopped = j != n; } - dsleep(.01); + usleep(10000); } close(fd); WriteString("\r\n"); diff --git a/tool/viz/viz.mk b/tool/viz/viz.mk index 7a7d72c28..45bb46df5 100644 --- a/tool/viz/viz.mk +++ b/tool/viz/viz.mk @@ -31,12 +31,14 @@ TOOL_VIZ_DIRECTDEPS = \ LIBC_NT_GDI32 \ LIBC_NT_KERNEL32 \ LIBC_NT_USER32 \ + LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ LIBC_SYSV_CALLS \ + LIBC_THREAD \ LIBC_TIME \ LIBC_TINYMATH \ LIBC_X \