From 54e6f564c18ddf15fb7b4b260b8dd10c789e39fa Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 14 May 2022 11:47:16 -0700 Subject: [PATCH 01/40] Let ctrl-c interrupt lua server pages in repl mode --- third_party/lua/lrepl.c | 8 ++++++++ third_party/lua/lrepl.h | 1 + tool/net/redbean.c | 26 +++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 482b0577e..afa16db70 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -380,6 +380,13 @@ int lua_loadline (lua_State *L) { } +void lua_sigint (lua_State *L, int sig) { + int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; + lua_sethook(L, lstop, flag, 1); +} + + + /* ** Function to be called at a C signal. Because a C signal cannot ** just change a Lua state (as there is no proper synchronization), @@ -387,6 +394,7 @@ int lua_loadline (lua_State *L) { ** interpreter. */ static void laction (int i) { + lua_sigint(globalL, i); int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; lua_sethook(globalL, lstop, flag, 1); } diff --git a/third_party/lua/lrepl.h b/third_party/lua/lrepl.h index 5de303a7a..304bc3ac4 100644 --- a/third_party/lua/lrepl.h +++ b/third_party/lua/lrepl.h @@ -28,6 +28,7 @@ extern linenoiseCompletionCallback *lua_repl_completions_callback; void lua_freerepl(void); int lua_loadline(lua_State *); void lua_l_print(lua_State *); +void lua_sigint(lua_State *, int); void lua_initrepl(lua_State *, const char *); int lua_report(lua_State *, int); int lua_runchunk(lua_State *, int, int); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 4063fef3a..15ffe1f82 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -42,6 +42,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/spinlock.h" #include "libc/log/backtrace.internal.h" @@ -102,6 +103,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/s.h" +#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" @@ -2465,13 +2467,28 @@ static ssize_t YieldGenerator(struct iovec v[3]) { return contentlength; } +static void OnLuaServerPageCtrlc(int i) { + lua_sigint(GL, i); +} + static int LuaCallWithYield(lua_State *L) { int status; // since yield may happen in OnHttpRequest and in ServeLua, // need to fully restart the yield generator; // the second set of headers is not going to be sent + struct sigaction sa, saold; lua_State *co = lua_newthread(L); - if ((status = LuaCallWithTrace(L, 0, 0, co)) == LUA_YIELD) { + if (__replmode) { + sa.sa_flags = SA_RESETHAND; + sa.sa_handler = OnLuaServerPageCtrlc; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, &saold); + } + status = LuaCallWithTrace(L, 0, 0, co); + if (__replmode) { + sigaction(SIGINT, &saold, 0); + } + if (status == LUA_YIELD) { CHECK_GT(lua_gettop(L), 0); // make sure that coroutine is anchored YL = co; generator = YieldGenerator; @@ -4924,10 +4941,10 @@ static int LuaLaunchBrowser(lua_State *L) { } static bool LuaRunAsset(const char *path, bool mandatory) { + int status; struct Asset *a; const char *code; size_t pathlen, codelen; - int status; pathlen = strlen(path); if ((a = GetAsset(path, pathlen))) { if ((code = FreeLater(LoadAsset(a, &codelen)))) { @@ -5310,7 +5327,7 @@ static void LuaInterpreter(lua_State *L) { if (status == -2) { if (errno == EINTR) { if ((sig = linenoiseGetInterrupt())) { - raise(sig); + kill(0, sig); } } fprintf(stderr, "i/o error: %m\n"); @@ -6742,6 +6759,9 @@ static int HandleReadline(void) { } else if (errno == EINTR) { errno = 0; VERBOSEF("(repl) interrupt"); + shutdownsig = SIGINT; + OnInt(SIGINT); + kill(0, SIGINT); return -1; } else if (errno == EAGAIN) { errno = 0; From 6bf6f9e37694ef54738e01907b73a5827a0b63d9 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 14 May 2022 21:13:45 -0700 Subject: [PATCH 02/40] Add compress and decompress examples --- examples/compress.c | 92 ++++++++++++++++++++++++++++++++++++++ examples/decompress.c | 100 ++++++++++++++++++++++++++++++++++++++++++ examples/greenbean.c | 15 +++++-- 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 examples/compress.c create mode 100644 examples/decompress.c diff --git a/examples/compress.c b/examples/compress.c new file mode 100644 index 000000000..800e9532f --- /dev/null +++ b/examples/compress.c @@ -0,0 +1,92 @@ +#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/assert.h" +#include "libc/errno.h" +#include "libc/log/check.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "third_party/zlib/zlib.h" + +#define CHUNK 4096 + +// clang-format off +// make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b +// clang-format on + +int compressor(int infd, int outfd) { + z_stream zs; + int rc, flush; + unsigned have; + unsigned char *inbuf; + unsigned char *outbuf; + inbuf = gc(valloc(CHUNK)); + outbuf = gc(valloc(CHUNK)); + zs.zalloc = 0; + zs.zfree = 0; + zs.opaque = 0; + rc = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (rc != Z_OK) return rc; + do { + rc = read(infd, inbuf, CHUNK); + if (rc == -1) { + deflateEnd(&zs); + return Z_ERRNO; + } + zs.avail_in = rc; + flush = !rc ? Z_FINISH : Z_SYNC_FLUSH; + zs.next_in = inbuf; + do { + zs.avail_out = CHUNK; + zs.next_out = outbuf; + rc = deflate(&zs, flush); + assert(rc != Z_STREAM_ERROR); + have = CHUNK - zs.avail_out; + if (write(outfd, outbuf, have) != have) { + deflateEnd(&zs); + return Z_ERRNO; + } + } while (!zs.avail_out); + assert(!zs.avail_in); + } while (flush != Z_FINISH); + assert(rc == Z_STREAM_END); + deflateEnd(&zs); + return Z_OK; +} + +const char *zerr(int rc) { + switch (rc) { + case Z_ERRNO: + return strerror(errno); + case Z_STREAM_ERROR: + return "invalid compression level"; + case Z_DATA_ERROR: + return "invalid or incomplete deflate data"; + case Z_MEM_ERROR: + return "out of memory"; + case Z_VERSION_ERROR: + return "zlib version mismatch!"; + default: + unreachable; + } +} + +int main(int argc, char *argv[]) { + int rc; + rc = compressor(0, 1); + if (rc == Z_OK) { + return 0; + } else { + fprintf(stderr, "error: compressor: %s\n", zerr(rc)); + return 1; + } +} diff --git a/examples/decompress.c b/examples/decompress.c new file mode 100644 index 000000000..a8b483b6d --- /dev/null +++ b/examples/decompress.c @@ -0,0 +1,100 @@ +#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/assert.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/stdio/stdio.h" +#include "third_party/zlib/zlib.h" + +#define CHUNK 4096 + +// clang-format off +// make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b +// clang-format on + +int decompressor(int infd, int outfd) { + int rc; + unsigned have; + z_stream zs; + unsigned char *inbuf; + unsigned char *outbuf; + inbuf = gc(valloc(CHUNK)); + outbuf = gc(valloc(CHUNK)); + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = 0; + zs.next_in = Z_NULL; + rc = inflateInit(&zs); + if (rc != Z_OK) return rc; + do { + rc = read(infd, inbuf, CHUNK); + if (rc == -1) { + inflateEnd(&zs); + return Z_ERRNO; + } + if (!rc) { + break; + } + zs.avail_in = rc; + zs.next_in = inbuf; + do { + zs.avail_out = CHUNK; + zs.next_out = outbuf; + rc = inflate(&zs, Z_SYNC_FLUSH); + assert(rc != Z_STREAM_ERROR); + switch (rc) { + case Z_NEED_DICT: + rc = Z_DATA_ERROR; + // fallthrough + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&zs); + return rc; + } + have = CHUNK - zs.avail_out; + if (write(outfd, outbuf, have) != have) { + inflateEnd(&zs); + return Z_ERRNO; + } + } while (!zs.avail_out); + } while (rc != Z_STREAM_END); + inflateEnd(&zs); + return rc == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + +const char *zerr(int rc) { + switch (rc) { + case Z_ERRNO: + return strerror(errno); + case Z_STREAM_ERROR: + return "invalid compression level"; + case Z_DATA_ERROR: + return "invalid or incomplete deflate data"; + case Z_MEM_ERROR: + return "out of memory"; + case Z_VERSION_ERROR: + return "zlib version mismatch!"; + default: + unreachable; + } +} + +int main(int argc, char *argv[]) { + int rc; + rc = decompressor(0, 1); + if (rc == Z_OK) { + return 0; + } else { + fprintf(stderr, "error: decompressor: %s\n", zerr(rc)); + return 1; + } +} diff --git a/examples/greenbean.c b/examples/greenbean.c index 840926388..72b761edd 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" +#include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" @@ -34,6 +35,7 @@ #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/map.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" @@ -141,6 +143,14 @@ int Worker(void *id) { char inbuf[1500], outbuf[512], *p, *q; int clientip, client, inmsglen, outmsglen; + __atomic_load(&closingtime, &itsover, __ATOMIC_SEQ_CST); + if (itsover) break; + + if (!IsLinux() && + poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) { + continue; + } + // wait for client connection clientaddrsize = sizeof(clientaddr); client = accept(server, &clientaddr, &clientaddrsize); @@ -154,8 +164,6 @@ int Worker(void *id) { // side-effect that the listening socket fails with EAGAIN, every // several seconds. we can use that to our advantage to check for // the ctrl-c shutdown event; otherwise, we retry the accept call - __atomic_load(&closingtime, &itsover, __ATOMIC_SEQ_CST); - if (itsover) break; continue; } @@ -274,7 +282,8 @@ int main(int argc, char *argv[]) { __atomic_load(&workers, &haveleft, __ATOMIC_SEQ_CST); if (!haveleft) break; __builtin_ia32_pause(); - if (usleep(HEARTBEAT * 1000) == -1 && closingtime) { + usleep(HEARTBEAT * 1000); + if (closingtime) { kprintf("\rgreenbean is shutting down...\n"); } } From c446af799e999bc09b6c9d3233d812f6f0bbd077 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 14 May 2022 23:17:22 -0700 Subject: [PATCH 03/40] Compress network file transfers --- tool/build/runit.c | 33 +++++++++++++++--- tool/build/runitd.c | 83 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/tool/build/runit.c b/tool/build/runit.c index 60e9e905e..63109d038 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -30,6 +30,7 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" @@ -51,6 +52,7 @@ #include "libc/x/x.h" #include "net/https/https.h" #include "third_party/mbedtls/ssl.h" +#include "third_party/zlib/zlib.h" #include "tool/build/lib/eztls.h" #include "tool/build/lib/psk.h" #include "tool/build/runit.h" @@ -306,6 +308,28 @@ TryAgain: freeaddrinfo(ai); } +static void Send(const void *output, size_t outputsize) { + int rc, have; + static bool once; + static z_stream zs; + static char zbuf[4096]; + if (!once) { + CHECK_EQ(Z_OK, deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)); + once = true; + } + zs.next_in = output; + zs.avail_in = outputsize; + do { + zs.avail_out = sizeof(zbuf); + zs.next_out = (unsigned char *)zbuf; + rc = deflate(&zs, Z_SYNC_FLUSH); + CHECK_NE(Z_STREAM_ERROR, rc); + have = sizeof(zbuf) - zs.avail_out; + CHECK_EQ(have, mbedtls_ssl_write(&ezssl, zbuf, have)); + } while (!zs.avail_out); +} + void SendRequest(void) { int fd; char *p; @@ -316,7 +340,7 @@ void SendRequest(void) { const char *name; unsigned char *hdr, *q; size_t progsize, namesize, hdrsize; - DEBUGF("running %s on %s", g_prog, g_hostname); + unsigned have; CHECK_NE(-1, (fd = open(g_prog, O_RDONLY))); CHECK_NE(-1, fstat(fd, &st)); CHECK_NE(MAP_FAILED, (p = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); @@ -332,10 +356,9 @@ void SendRequest(void) { q = WRITE32BE(q, crc); q = mempcpy(q, name, namesize); assert(hdrsize == q - hdr); - CHECK_EQ(hdrsize, mbedtls_ssl_write(&ezssl, hdr, hdrsize)); - for (i = 0; i < progsize; i += rc) { - CHECK_GT((rc = mbedtls_ssl_write(&ezssl, p + i, progsize - i)), 0); - } + DEBUGF("running %s on %s", g_prog, g_hostname); + Send(hdr, hdrsize); + Send(p, progsize); CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0)); CHECK_NE(-1, munmap(p, st.st_size)); CHECK_NE(-1, close(fd)); diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 71d4bb3c6..1d1e6bbda 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -51,6 +51,7 @@ #include "net/https/https.h" #include "third_party/getopt/getopt.h" #include "third_party/mbedtls/ssl.h" +#include "third_party/zlib/zlib.h" #include "tool/build/lib/eztls.h" #include "tool/build/lib/psk.h" #include "tool/build/runit.h" @@ -256,16 +257,78 @@ void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf, CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0)); } -void Recv(void *p, size_t n) { - size_t i, rc; - for (i = 0; i < n; i += rc) { - do { - rc = mbedtls_ssl_read(&ezssl, (char *)p + i, n - i); - DEBUGF("read(%ld)", rc); - } while (rc == MBEDTLS_ERR_SSL_WANT_READ); - if (rc <= 0) TlsDie("read failed", rc); +void Recv(void *output, size_t outputsize) { + int rc; + ssize_t tx, chunk, received; + static bool once; + static int zstatus; + static char buf[4096]; + static z_stream zs; + static struct { + size_t off; + size_t len; + size_t cap; + char *data; + } rbuf; + if (!once) { + CHECK_EQ(Z_OK, inflateInit(&zs)); + once = true; + } + for (;;) { + if (rbuf.len >= outputsize) { + tx = MIN(outputsize, rbuf.len); + memcpy(output, rbuf.data + rbuf.off, outputsize); + rbuf.len -= outputsize; + rbuf.off += outputsize; + // trim dymanic buffer once it empties + if (!rbuf.len) { + rbuf.off = 0; + rbuf.cap = 4096; + rbuf.data = realloc(rbuf.data, rbuf.cap); + } + return; + } + if (zstatus == Z_STREAM_END) { + FATALF("recv zlib unexpected eof"); + } + // get another fixed-size data packet from network + // pass along error conditions to caller + // pass along eof condition to zlib + received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf)); + if (received < 0) TlsDie("read failed", received); + // decompress packet completely + // into a dynamical size buffer + zs.avail_in = received; + zs.next_in = (unsigned char *)buf; + CHECK_EQ(Z_OK, zstatus); + do { + // make sure we have a reasonable capacity for zlib output + if (rbuf.cap - (rbuf.off + rbuf.len) < sizeof(buf)) { + rbuf.cap += sizeof(buf); + rbuf.data = realloc(rbuf.data, rbuf.cap); + } + // inflate packet, which naturally can be much larger + // permit zlib no delay flushes that come from sender + zs.next_out = (unsigned char *)rbuf.data + (rbuf.off + rbuf.len); + zs.avail_out = chunk = rbuf.cap - (rbuf.off + rbuf.len); + zstatus = inflate(&zs, Z_SYNC_FLUSH); + CHECK_NE(Z_STREAM_ERROR, zstatus); + switch (zstatus) { + case Z_NEED_DICT: + zstatus = Z_DATA_ERROR; // make negative + // fallthrough + case Z_DATA_ERROR: + case Z_MEM_ERROR: + FATALF("tls recv zlib hard error %d", zstatus); + case Z_BUF_ERROR: + zstatus = Z_OK; // harmless? nothing for inflate to do + break; // it probably just our wraparound eof + default: + rbuf.len += chunk - zs.avail_out; + break; + } + } while (!zs.avail_out); } - DEBUGF("Recv(%ld)", n); } void HandleClient(void) { @@ -293,6 +356,7 @@ void HandleClient(void) { EzHandshake(); addrstr = gc(DescribeAddress(&addr)); DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr); + Recv(msg, sizeof(msg)); CHECK_EQ(RUNITD_MAGIC, READ32BE(msg)); CHECK_EQ(kRunitExecute, msg[4]); @@ -459,7 +523,6 @@ void Daemonize(void) { int main(int argc, char *argv[]) { int i; - ShowCrashReports(); SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk()); /* __log_level = kLogDebug; */ GetOpts(argc, argv); From e5e141d9b55d78a5184a71f2bae0d64c5d1eb2b2 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Sat, 14 May 2022 23:25:54 -0700 Subject: [PATCH 04/40] Update redbean unix module documentation for consistency (#409) --- tool/net/help.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tool/net/help.txt b/tool/net/help.txt index 1c74ead80..f31c9264e 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1726,7 +1726,7 @@ UNIX MODULE Creates fifo which enables communication between processes. - `flags` can have any of + `flags` may have any combination (using bitwise OR) of: - `O_CLOEXEC`: Automatically close file descriptor upon execve() @@ -2328,7 +2328,7 @@ UNIX MODULE - `SOCK_RDM` - `SOCK_SEQPACKET` - You may bitwise or any of the following into `type`: + You may bitwise OR any of the following into `type`: - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` @@ -2355,7 +2355,7 @@ UNIX MODULE - `SOCK_DGRAM` - `SOCK_SEQPACKET` - You may bitwise or any of the following into `type`: + You may bitwise OR any of the following into `type`: - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` @@ -2484,7 +2484,7 @@ UNIX MODULE Returns `EINVAL` if settings other than the above are used. - Returns `ENOSYS` if setting isn't supported by the host o/s. + Returns `ENOSYS` if setting isn't supported by the host OS. unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) ├─→ {[fd:int]=revents:int, ...} @@ -2519,7 +2519,7 @@ UNIX MODULE Accepts new client socket descriptor for a listening tcp socket. - `flags` can have any of: + `flags` may have any combination (using bitwise OR) of: - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` @@ -2550,7 +2550,7 @@ UNIX MODULE ├─→ data:str └─→ nil, unix.Errno - `flags` can have: + `flags` may have any combination (using bitwise OR) of: - `MSG_WAITALL` - `MSG_DONTROUTE` @@ -2561,7 +2561,7 @@ UNIX MODULE ├─→ data:str, ip:uint32, port:uint16 └─→ nil, unix.Errno - `flags` can have: + `flags` may have any combination (using bitwise OR) of: - `MSG_WAITALL` - `MSG_DONTROUTE` @@ -2575,7 +2575,7 @@ UNIX MODULE This is the same as `write` except it has a `flags` argument that's intended for sockets. - `flags` may have any of: + `flags` may have any combination (using bitwise OR) of: - `MSG_OOB` - `MSG_DONTROUTE` @@ -2588,7 +2588,7 @@ UNIX MODULE This is useful for sending messages over UDP sockets to specific addresses. - `flags` may have any of: + `flags` may have any combination (using bitwise OR) of: - `MSG_OOB` - `MSG_DONTROUTE` @@ -2617,7 +2617,7 @@ UNIX MODULE `how` can be one of: - - `SIG_BLOCK`: bitwise ors `mask` into set of blocked signals + - `SIG_BLOCK`: applies `mask` to set of blocked signals using bitwise OR - `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals - `SIG_SETMASK`: replaces process signal mask with `mask` @@ -2766,11 +2766,11 @@ UNIX MODULE - `RLIMIT_CPU` causes `SIGXCPU` to be sent to the process when the soft limit on CPU time is exceeded, and the process is destroyed when the hard limit is exceeded. It works everywhere but Windows - where it should be possible to poll getrusage() with setitimer() + where it should be possible to poll getrusage() with setitimer(). - `RLIMIT_FSIZE` causes `SIGXFSZ` to sent to the process when the soft limit on file size is exceeded and the process is destroyed - when the hard limit is exceeded. It works everywhere but Windows + when the hard limit is exceeded. It works everywhere but Windows. - `RLIMIT_NPROC` limits the number of simultaneous processes and it should work on all platforms except Windows. Please be advised it @@ -2778,7 +2778,7 @@ UNIX MODULE as a whole. - `RLIMIT_NOFILE` limits the number of open file descriptors and it - should work on all platforms except Windows (TODO) + should work on all platforms except Windows (TODO). If a limit isn't supported by the host platform, it'll be set to 127. On most platforms these limits are enforced by the kernel and From 91ee2b19d44c8b874ac332e593b5e63dcb583f99 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 15 May 2022 09:14:48 -0700 Subject: [PATCH 05/40] Polish greenbean example a bit more Windows support for this example is still a work in progress. It's encountering some unusual crashes. Thank you Chris Wellons for the cool synchronization code too! --- examples/greenbean.c | 194 ++++++++++++++++++++++------------- libc/calls/strace.internal.h | 2 +- libc/runtime/winmain.greg.c | 12 ++- libc/sysv/consts.sh | 18 ++-- 4 files changed, 145 insertions(+), 81 deletions(-) diff --git a/examples/greenbean.c b/examples/greenbean.c index 72b761edd..29b3c3596 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigset.h" @@ -27,6 +28,7 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/str/str.h" @@ -71,25 +73,25 @@ * Like redbean, greenbean has superior performance too, with an * advantage on benchmarks biased towards high connection counts * - * $ sudo wrk -c 300 -t 32 --latency http://127.0.0.1:8080/ - * Running 10s test @ http://127.0.0.1:8080/ + * $ sudo wrk -c 300 -t 32 --latency http://10.10.10.124:8080/ + * Running 10s test @ http://10.10.10.124:8080/ * 32 threads and 300 connections * Thread Stats Avg Stdev Max +/- Stdev - * Latency 36.21us 133.39us 8.10ms 98.52% - * Req/Sec 73.24k 28.92k 131.06k 47.49% + * Latency 1.07ms 8.27ms 138.55ms 98.58% + * Req/Sec 37.98k 12.61k 117.65k 80.11% * Latency Distribution - * 50% 22.00us - * 75% 29.00us - * 90% 40.00us - * 99% 333.00us - * 4356560 requests in 4.62s, 1.29GB read - * Requests/sec: 942663.73 - * Transfer/sec: 284.98MB + * 50% 200.00us + * 75% 227.00us + * 90% 303.00us + * 99% 32.46ms + * 10033090 requests in 8.31s, 2.96GB read + * Requests/sec: 1207983.58 + * Transfer/sec: 365.19MB * */ -#define THREADS 32 -#define HEARTBEAT 500 +#define THREADS 512 +#define HEARTBEAT 100 #define KEEPALIVE 5000 #define LOGGING 0 @@ -98,23 +100,106 @@ "Referrer-Policy: origin\r\n" \ "Cache-Control: private; max-age=0\r\n" -int workers; -int barrier; +//////////////////////////////////////////////////////////////////////////////// +// BEGIN: Chris Wellons's Public Domain GNU Atomics Library + +#define BARRIER_INC(x) __atomic_add_fetch(x, 1, __ATOMIC_SEQ_CST) +#define BARRIER_GET(x) __atomic_load_n(x, __ATOMIC_SEQ_CST) +#define ATOMIC_LOAD(q) __atomic_load_n(q, __ATOMIC_ACQUIRE) +#define ATOMIC_RLOAD(q) __atomic_load_n(q, __ATOMIC_RELAXED) +#define ATOMIC_STORE(q, v) __atomic_store_n(q, v, __ATOMIC_RELEASE) +#define ATOMIC_ADD(q, c) __atomic_add_fetch(q, c, __ATOMIC_RELEASE) +#define ATOMIC_AND(q, m) __atomic_and_fetch(q, m, __ATOMIC_RELEASE) +#define ATOMIC_CAS(q, e, d) \ + __atomic_compare_exchange_n(q, e, d, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +// Return the array index for then next value to be pushed. The size of this +// array must be (1 << exp) elements. Write the value into this array index, +// then commit it. With a single-consumer queue, this element store need not +// be atomic. The value will appear in the queue after the commit. Returns +// -1 if the queue is full. +static int queue_push(uint32_t *q, int exp) { + uint32_t r = ATOMIC_LOAD(q); + int mask = (1u << exp) - 1; + int head = r & mask; + int tail = r >> 16 & mask; + int next = (head + 1u) & mask; + if (r & 0x8000) { // avoid overflow on commit + ATOMIC_AND(q, ~0x8000); + } + return next == tail ? -1 : head; +} + +// Commits and completes the push operation. Do this after storing into the +// array. This operation cannot fail. +static void queue_push_commit(uint32_t *q) { + ATOMIC_ADD(q, 1); +} + +// Return the array index for the next value to be popped. The size of this +// array must be (1 << exp) elements. Read from this array index, then +// commit the pop. This element load need not be atomic. The value will be +// removed from the queue after the commit. Returns -1 if the queue is +// empty. +static int queue_pop(uint32_t *q, int exp) { + uint32_t r = ATOMIC_LOAD(q); + int mask = (1u << exp) - 1; + int head = r & mask; + int tail = r >> 16 & mask; + return head == tail ? -1 : tail; +} + +// Commits and completes the pop operation. Do this after loading from the +// array. This operation cannot fail. +static void queue_pop_commit(uint32_t *q) { + ATOMIC_ADD(q, 0x10000); +} + +// Like queue_pop() but for multiple-consumer queues. The element load must +// be atomic since it is concurrent with the producer's push, though it can +// use a relaxed memory order. The loaded value must not be used unless the +// commit is successful. Stores a temporary "save" to be used at commit. +static int queue_mpop(uint32_t *q, int exp, uint32_t *save) { + uint32_t r = *save = ATOMIC_LOAD(q); + int mask = (1u << exp) - 1; + int head = r & mask; + int tail = r >> 16 & mask; + return head == tail ? -1 : tail; +} + +// Like queue_pop_commit() but for multiple-consumer queues. It may fail if +// another consumer pops concurrently, in which case the pop must be retried +// from the beginning. +static bool queue_mpop_commit(uint32_t *q, uint32_t save) { + return ATOMIC_CAS(q, &save, save + 0x10000); +} + +// Spin-lock barrier for n threads, where n is a power of two. +// Initialize *barrier to zero. +static void barrier_waitn(int *barrier, int n) { + int v = BARRIER_INC(barrier); + if (v & (n - 1)) { + for (v &= n; (BARRIER_GET(barrier) & n) == v;) { + donothing; + } + } +} + +// END: Chris Wellons's Public Domain GNU Atomics Library +//////////////////////////////////////////////////////////////////////////////// + +int barrier1; +int itsbegun; int closingtime; +int barrier2; +int itsdone; int Worker(void *id) { - int server, itsover, ready, yes = 1; + int server, yes = 1; - // announce to the main process this has spawned - kprintf(" #%.2ld", (intptr_t)id); - __atomic_add_fetch(&workers, 1, __ATOMIC_SEQ_CST); - - // wait for all threads to spawn before we proceed - for (;;) { - __atomic_load(&barrier, &ready, __ATOMIC_SEQ_CST); - if (ready) break; - __builtin_ia32_pause(); - } + kprintf(" %d", id); + barrier_waitn(&barrier1, THREADS); + itsbegun = true; // load balance incoming connections for port 8080 across all threads // hangup on any browser clients that lag for more than a few seconds @@ -131,7 +216,7 @@ int Worker(void *id) { CHECK_EQ(0, listen(server, 10)); // connection loop - for (;;) { + while (!closingtime) { struct tm tm; int64_t unixts; struct Url url; @@ -143,15 +228,8 @@ int Worker(void *id) { char inbuf[1500], outbuf[512], *p, *q; int clientip, client, inmsglen, outmsglen; - __atomic_load(&closingtime, &itsover, __ATOMIC_SEQ_CST); - if (itsover) break; - - if (!IsLinux() && - poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) { - continue; - } - // wait for client connection + if (poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) continue; clientaddrsize = sizeof(clientaddr); client = accept(server, &clientaddr, &clientaddrsize); @@ -163,7 +241,7 @@ int Worker(void *id) { // inherited by the accepted sockets, but using them also has the // side-effect that the listening socket fails with EAGAIN, every // several seconds. we can use that to our advantage to check for - // the ctrl-c shutdown event; otherwise, we retry the accept call + // the ctrl-c shutdowne event; otherwise, we retry the accept call continue; } @@ -179,7 +257,7 @@ int Worker(void *id) { #if LOGGING // log the incoming http message clientip = ntohl(clientaddr.sin_addr.s_addr); - kprintf("#%.2ld get some %d.%d.%d.%d:%d %#.*s\n", (intptr_t)id, + kprintf("#%.4x get some %d.%d.%d.%d:%d %#.*s\n", (intptr_t)id, (clientip & 0xff000000) >> 030, (clientip & 0x00ff0000) >> 020, (clientip & 0x0000ff00) >> 010, (clientip & 0x000000ff) >> 000, ntohs(clientaddr.sin_port), msg.uri.b - msg.uri.a, @@ -239,8 +317,9 @@ int Worker(void *id) { // inform the parent that this clone has finished close(server); - kprintf(" #%.2ld", (intptr_t)id); - __atomic_sub_fetch(&workers, 1, __ATOMIC_SEQ_CST); + kprintf(" %d", id); + barrier_waitn(&barrier2, THREADS); + itsdone = true; return 0; } @@ -249,45 +328,20 @@ void OnCtrlC(int sig) { } int main(int argc, char *argv[]) { + /* ShowCrashReports(); */ int64_t loadtzdbearly; - int i, gotsome, haveleft, ready = 1; - - ShowCrashReports(); kprintf("welcome to greenbean\n"); gmtime(&loadtzdbearly); - - // spawn a bunch of threads - for (i = 0; i < THREADS; ++i) { + for (int i = 0; i < THREADS; ++i) { void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); clone(Worker, stack, 65536, CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(intptr_t)i, 0, 0, 0, 0); } - - // wait for all threads to spawn - for (;;) { - __atomic_load(&workers, &gotsome, __ATOMIC_SEQ_CST); - if (workers == THREADS) break; - __builtin_ia32_pause(); - } - - // all threads are spawned so unleash the barrier - kprintf("\ngreenbean is ready to go\n"); + while (!ATOMIC_LOAD(&itsbegun)) usleep(HEARTBEAT * 1000); sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0); - __atomic_store(&barrier, &ready, __ATOMIC_SEQ_CST); - - // main process does nothing until it's closing time - for (;;) { - __atomic_load(&workers, &haveleft, __ATOMIC_SEQ_CST); - if (!haveleft) break; - __builtin_ia32_pause(); - usleep(HEARTBEAT * 1000); - if (closingtime) { - kprintf("\rgreenbean is shutting down...\n"); - } - } - - kprintf("\n"); - kprintf("thank you for flying greenbean\n"); + kprintf("\nit's begun\n"); + while (!ATOMIC_LOAD(&itsdone)) usleep(HEARTBEAT * 1000); + kprintf("\nthank you for flying greenbean\n"); } diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 1051c0044..fbfa984a6 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -8,7 +8,7 @@ #define _KERNTRACE 0 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _NTTRACE 1 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 74bc3e64d..4d737dffa 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -121,6 +121,16 @@ forceinline void MakeLongDoubleLongAgain(void) { asm volatile("fldcw\t%0" : /* no outputs */ : "m"(x87cw)); } +// https://nullprogram.com/blog/2022/02/18/ +static inline char16_t *MyCommandLine(void) { + void *cmd; + asm("mov\t%%gs:(0x60),%0\n" + "mov\t0x20(%0),%0\n" + "mov\t0x78(%0),%0\n" + : "=r"(cmd)); + return cmd; +} + static inline size_t StrLen16(const char16_t *s) { size_t n; for (n = 0;; ++n) { @@ -271,7 +281,7 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, #if !IsTiny() __wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash); #endif - cmdline = GetCommandLine(); + cmdline = MyCommandLine(); #ifdef SYSDEBUG /* sloppy flag-only check for early initialization */ if (__strstr16(cmdline, u"--strace")) ++__strace; diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 2ad3f7c43..3be697aa8 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1158,18 +1158,18 @@ syscon ms MS_INVALIDATE 2 2 2 4 2 0 # statvfs() flags # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon statvfs ST_NOSUID 2 2 2 2 2 0 # unix consensus syscon statvfs ST_RDONLY 1 1 1 1 1 0 # unix consensus +syscon statvfs ST_NOSUID 2 2 2 2 2 0 # unix consensus +syscon statvfs ST_NODEV 4 0 0 0 0x00000010 0 +syscon statvfs ST_NOEXEC 8 0 0 0 4 0 +syscon statvfs ST_SYNCHRONOUS 16 0 0 0 2 0 syscon statvfs ST_APPEND 0x0100 0 0 0 0 0 syscon statvfs ST_IMMUTABLE 0x0200 0 0 0 0 0 -syscon statvfs ST_MANDLOCK 0x40 0 0 0 0 0 +syscon statvfs ST_MANDLOCK 0x0040 0 0 0 0 0 syscon statvfs ST_NOATIME 0x0400 0 0 0x04000000 0 0 -syscon statvfs ST_NODEV 4 0 0 0 0x00000010 0 syscon statvfs ST_NODIRATIME 0x0800 0 0 0 0 0 -syscon statvfs ST_NOEXEC 8 0 0 0 4 0 +syscon statvfs ST_WRITE 0x0080 0 0 0 0 0 syscon statvfs ST_RELATIME 0x1000 0 0 0 0x00020000 0 -syscon statvfs ST_SYNCHRONOUS 0x10 0 0 0 2 0 -syscon statvfs ST_WRITE 0x80 0 0 0 0 0 # sendfile() flags # @@ -1442,7 +1442,7 @@ syscon termios IUTF8 0b0100000000000000 0b0100000000000000 0 0 0 0b010 # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios OPOST 0b0000000000000001 0b000000000000000001 0b000000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_oflag&=~OPOST disables output processing magic, e.g. MULTICS newlines -syscon termios OLCUC 0b0000000000000010 0 0 0b0000000000100000 0 0b0000000000000010 # termios.c_oflag|=OLCUC maps a-z → A-Z output +syscon termios OLCUC 0b0000000000000010 0 0 0b0000000000100000 0 0b0000000000000010 # termios.c_oflag|=OLCUC maps a-z → A-Z output (SHOUTING) syscon termios ONLCR 0b0000000000000100 0b000000000000000010 0b000000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000100 # termios.c_oflag|=ONLCR map \n → \r\n output (MULTICS newline) and requires OPOST syscon termios OCRNL 0b0000000000001000 0b000000000000010000 0b000000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000001000 # termios.c_oflag|=OCRNL maps \r → \n output syscon termios ONOCR 0b0000000000010000 0b000000000000100000 0b000000000000100000 0b0000000001000000 0b0000000001000000 0b0000000000010000 # termios.c_oflag|=ONOCR maps \r → ∅ output iff column 0 @@ -1478,14 +1478,14 @@ syscon termios FF1 0b1000000000000000 0b000100000000000000 0b0001000000000 # Teletypewriter Special Control Character Assignments # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon termios VMIN 6+1 16 16 16 16 6 # termios.c_cc[VMIN]=𝑥 in non-canonical mode can be set to 0 for non-blocking reads, 1 for single character raw mode reads, or higher to buffer +syscon termios VTIME 5+1 17 17 17 17 5 # termios.c_cc[VTIME]=𝑥 sets non-canonical read timeout to 𝑥×𝟷𝟶𝟶ms which is needed when entering escape sequences manually with the escape key syscon termios NCCS 20 20 20 20 20 20 # ARRAYLEN(termios.c_cc); we schlep c_line into c_cc on linux syscon termios VINTR 0+1 8 8 8 8 0 # termios.c_cc[VINTR]=𝑥 syscon termios VQUIT 1+1 9 9 9 9 1 # termios.c_cc[VQUIT]=𝑥 syscon termios VERASE 2+1 3 3 3 3 2 # termios.c_cc[VERASE]=𝑥 syscon termios VKILL 3+1 5 5 5 5 3 # termios.c_cc[VKILL]=𝑥 syscon termios VEOF 4+1 0 0 0 0 4 # termios.c_cc[VEOF]=𝑥 -syscon termios VTIME 5+1 17 17 17 17 5 # termios.c_cc[VTIME]=𝑥 sets non-canonical read timeout to 𝑥×𝟷𝟶𝟶ms which is needed when entering escape sequences manually with the escape key -syscon termios VMIN 6+1 16 16 16 16 6 # termios.c_cc[VMIN]=𝑥 in non-canonical mode can be set to 0 for non-blocking reads, 1 for single character raw mode reads, or higher to buffer syscon termios VSWTC 7+1 0 0 0 0 7 # termios.c_cc[VSWTC]=𝑥 syscon termios VSTART 8+1 12 12 12 12 8 # termios.c_cc[VSTART]=𝑥 syscon termios VSTOP 9+1 13 13 13 13 9 # termios.c_cc[VSTOP]=𝑥 From 55de4ca6b5971acd60912751a7dc320ec417eec9 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 16 May 2022 13:20:08 -0700 Subject: [PATCH 06/40] Support thread local storage --- examples/crashreport.c | 1 + examples/examples.mk | 8 - examples/greenbean.c | 170 ++++++----------- libc/calls/calls.mk | 13 +- libc/calls/{execve-nt.c => execve-nt.greg.c} | 67 ++++++- libc/calls/execve.c | 5 - libc/calls/internal.h | 1 + libc/calls/ntcontext2linux.c | 9 +- libc/calls/sig2.c | 11 +- libc/calls/sigenter-freebsd.c | 20 +- libc/calls/sigenter-netbsd.c | 13 +- libc/calls/sigenter-openbsd.c | 12 +- libc/calls/sigenter-xnu.c | 23 +-- ...rocmask-sysv.c => sigprocmask-sysv.greg.c} | 0 libc/calls/wincrash.c | 2 +- libc/errno.h | 6 +- libc/fmt/sprintf.c | 2 + libc/intrin/intrin.mk | 1 + libc/intrin/kprintf.greg.c | 1 + libc/intrin/spinlock.h | 2 +- libc/intrin/threaded.internal.h | 1 + libc/intrin/tls.greg.c | 3 + libc/log/backtrace2.greg.c | 11 +- libc/log/oncrash.c | 1 + libc/macros.internal.inc | 9 + libc/nexgen32e/hastls.c | 21 +++ libc/nexgen32e/nexgen32e.mk | 3 +- libc/{intrin => nexgen32e}/threaded.c | 0 libc/runtime/arch_prctl.c | 3 +- libc/runtime/{clone.c => clone.greg.c} | 109 +++++++---- libc/runtime/ftracer.c | 4 +- libc/runtime/getinstructionlengths.c | 59 ++++++ libc/runtime/hook.greg.c | 130 ++++++------- libc/runtime/morph.greg.c | 79 ++++++++ libc/runtime/mprotect.greg.c | 9 +- libc/runtime/runtime.h | 14 +- libc/runtime/runtime.mk | 6 +- libc/runtime/vfork.S | 4 +- libc/runtime/winmain.greg.c | 2 +- libc/stubs/ld.S | 6 + libc/sysv/errfuns/e2big.S | 8 +- libc/sysv/errfuns/eacces.S | 8 +- libc/sysv/errfuns/eaddrinuse.S | 8 +- libc/sysv/errfuns/eaddrnotavail.S | 8 +- libc/sysv/errfuns/eadv.S | 8 +- libc/sysv/errfuns/eafnosupport.S | 8 +- libc/sysv/errfuns/eagain.S | 8 +- libc/sysv/errfuns/ealready.S | 8 +- libc/sysv/errfuns/ebade.S | 8 +- libc/sysv/errfuns/ebadf.S | 8 +- libc/sysv/errfuns/ebadfd.S | 8 +- libc/sysv/errfuns/ebadmsg.S | 8 +- libc/sysv/errfuns/ebadr.S | 8 +- libc/sysv/errfuns/ebadrqc.S | 8 +- libc/sysv/errfuns/ebadslt.S | 8 +- libc/sysv/errfuns/ebusy.S | 8 +- libc/sysv/errfuns/ecanceled.S | 8 +- libc/sysv/errfuns/echild.S | 8 +- libc/sysv/errfuns/echrng.S | 8 +- libc/sysv/errfuns/ecomm.S | 8 +- libc/sysv/errfuns/econnaborted.S | 8 +- libc/sysv/errfuns/econnrefused.S | 8 +- libc/sysv/errfuns/econnreset.S | 8 +- libc/sysv/errfuns/edeadlk.S | 8 +- libc/sysv/errfuns/edestaddrreq.S | 8 +- libc/sysv/errfuns/edom.S | 8 +- libc/sysv/errfuns/edotdot.S | 8 +- libc/sysv/errfuns/edquot.S | 8 +- libc/sysv/errfuns/eexist.S | 8 +- libc/sysv/errfuns/efault.S | 8 +- libc/sysv/errfuns/efbig.S | 8 +- libc/sysv/errfuns/ehostdown.S | 8 +- libc/sysv/errfuns/ehostunreach.S | 8 +- libc/sysv/errfuns/ehwpoison.S | 8 +- libc/sysv/errfuns/eidrm.S | 8 +- libc/sysv/errfuns/eilseq.S | 8 +- libc/sysv/errfuns/einprogress.S | 8 +- libc/sysv/errfuns/eintr.S | 8 +- libc/sysv/errfuns/einval.S | 8 +- libc/sysv/errfuns/eio.S | 8 +- libc/sysv/errfuns/eisconn.S | 8 +- libc/sysv/errfuns/eisdir.S | 8 +- libc/sysv/errfuns/eisnam.S | 8 +- libc/sysv/errfuns/ekeyexpired.S | 8 +- libc/sysv/errfuns/ekeyrejected.S | 8 +- libc/sysv/errfuns/ekeyrevoked.S | 8 +- libc/sysv/errfuns/el2hlt.S | 8 +- libc/sysv/errfuns/el2nsync.S | 8 +- libc/sysv/errfuns/el3hlt.S | 8 +- libc/sysv/errfuns/el3rst.S | 8 +- libc/sysv/errfuns/elibacc.S | 8 +- libc/sysv/errfuns/elibbad.S | 8 +- libc/sysv/errfuns/elibexec.S | 8 +- libc/sysv/errfuns/elibmax.S | 8 +- libc/sysv/errfuns/elibscn.S | 8 +- libc/sysv/errfuns/elnrng.S | 8 +- libc/sysv/errfuns/eloop.S | 8 +- libc/sysv/errfuns/emediumtype.S | 8 +- libc/sysv/errfuns/emfile.S | 8 +- libc/sysv/errfuns/emlink.S | 8 +- libc/sysv/errfuns/emsgsize.S | 8 +- libc/sysv/errfuns/emultihop.S | 8 +- libc/sysv/errfuns/enametoolong.S | 8 +- libc/sysv/errfuns/enavail.S | 8 +- libc/sysv/errfuns/enetdown.S | 8 +- libc/sysv/errfuns/enetreset.S | 8 +- libc/sysv/errfuns/enetunreach.S | 8 +- libc/sysv/errfuns/enfile.S | 8 +- libc/sysv/errfuns/enoano.S | 8 +- libc/sysv/errfuns/enobufs.S | 8 +- libc/sysv/errfuns/enocsi.S | 8 +- libc/sysv/errfuns/enodata.S | 8 +- libc/sysv/errfuns/enodev.S | 8 +- libc/sysv/errfuns/enoent.S | 8 +- libc/sysv/errfuns/enoexec.S | 8 +- libc/sysv/errfuns/enokey.S | 8 +- libc/sysv/errfuns/enolck.S | 8 +- libc/sysv/errfuns/enolink.S | 8 +- libc/sysv/errfuns/enomedium.S | 8 +- libc/sysv/errfuns/enomem.S | 8 +- libc/sysv/errfuns/enomsg.S | 8 +- libc/sysv/errfuns/enonet.S | 8 +- libc/sysv/errfuns/enopkg.S | 8 +- libc/sysv/errfuns/enoprotoopt.S | 8 +- libc/sysv/errfuns/enospc.S | 8 +- libc/sysv/errfuns/enosr.S | 8 +- libc/sysv/errfuns/enostr.S | 8 +- libc/sysv/errfuns/enosys.S | 8 +- libc/sysv/errfuns/enotblk.S | 8 +- libc/sysv/errfuns/enotconn.S | 8 +- libc/sysv/errfuns/enotdir.S | 8 +- libc/sysv/errfuns/enotempty.S | 8 +- libc/sysv/errfuns/enotnam.S | 8 +- libc/sysv/errfuns/enotrecoverable.S | 8 +- libc/sysv/errfuns/enotsock.S | 8 +- libc/sysv/errfuns/enotsup.S | 8 +- libc/sysv/errfuns/enotty.S | 8 +- libc/sysv/errfuns/enotuniq.S | 8 +- libc/sysv/errfuns/enxio.S | 8 +- libc/sysv/errfuns/eopnotsupp.S | 8 +- libc/sysv/errfuns/eoverflow.S | 8 +- libc/sysv/errfuns/eownerdead.S | 8 +- libc/sysv/errfuns/eperm.S | 8 +- libc/sysv/errfuns/epfnosupport.S | 8 +- libc/sysv/errfuns/epipe.S | 8 +- libc/sysv/errfuns/eproto.S | 8 +- libc/sysv/errfuns/eprotonosupport.S | 8 +- libc/sysv/errfuns/eprototype.S | 8 +- libc/sysv/errfuns/erange.S | 8 +- libc/sysv/errfuns/eremchg.S | 8 +- libc/sysv/errfuns/eremote.S | 8 +- libc/sysv/errfuns/eremoteio.S | 8 +- libc/sysv/errfuns/erestart.S | 8 +- libc/sysv/errfuns/erfkill.S | 8 +- libc/sysv/errfuns/erofs.S | 8 +- libc/sysv/errfuns/eshutdown.S | 8 +- libc/sysv/errfuns/esocktnosupport.S | 8 +- libc/sysv/errfuns/espipe.S | 8 +- libc/sysv/errfuns/esrch.S | 8 +- libc/sysv/errfuns/esrmnt.S | 8 +- libc/sysv/errfuns/estale.S | 8 +- libc/sysv/errfuns/estrpipe.S | 8 +- libc/sysv/errfuns/etime.S | 8 +- libc/sysv/errfuns/etimedout.S | 8 +- libc/sysv/errfuns/etoomanyrefs.S | 8 +- libc/sysv/errfuns/etxtbsy.S | 8 +- libc/sysv/errfuns/euclean.S | 8 +- libc/sysv/errfuns/eunatch.S | 8 +- libc/sysv/errfuns/eusers.S | 8 +- libc/sysv/errfuns/exdev.S | 8 +- libc/sysv/errfuns/exfull.S | 8 +- libc/{nexgen32e/errno.c => sysv/errno.greg.c} | 31 ++- libc/sysv/gen.sh | 7 +- libc/sysv/macros.internal.h | 1 + libc/sysv/systemfive.S | 4 +- libc/sysv/sysv.mk | 5 + libc/time/localtime.c | 42 ++--- libc/time/strftime.c | 1 + libc/time/time.mk | 8 - net/http/formathttpdatetime.c | 9 + test/libc/runtime/clone_test.c | 43 ++++- test/net/http/formathttpdatetime_test.c | 51 +++++ third_party/libcxx/libcxx.mk | 2 +- third_party/libcxx/random.cc | 10 +- third_party/libcxx/stdexcept_default.hh | 6 - third_party/libcxx/system_error.cc | 4 - third_party/lua/lua.mk | 1 + third_party/python/python.mk | 4 + third_party/python/repl.c | 10 + third_party/xed/xed.mk | 6 +- third_party/zlib/gz/gzread.c | 4 + tool/args/args.c | 13 +- tool/args/args.mk | 1 + tool/build/lib/errnos.S | 178 +++++++++--------- tool/emacs/cosmo-stuff.el | 9 + tool/net/lunix.c | 14 +- tool/net/redbean.c | 4 +- 197 files changed, 1483 insertions(+), 874 deletions(-) rename libc/calls/{execve-nt.c => execve-nt.greg.c} (59%) rename libc/calls/{sigprocmask-sysv.c => sigprocmask-sysv.greg.c} (100%) create mode 100644 libc/nexgen32e/hastls.c rename libc/{intrin => nexgen32e}/threaded.c (100%) rename libc/runtime/{clone.c => clone.greg.c} (87%) create mode 100644 libc/runtime/getinstructionlengths.c create mode 100644 libc/runtime/morph.greg.c rename libc/{nexgen32e/errno.c => sysv/errno.greg.c} (76%) create mode 100644 test/net/http/formathttpdatetime_test.c diff --git a/examples/crashreport.c b/examples/crashreport.c index 03f0650ff..558b6a28a 100644 --- a/examples/crashreport.c +++ b/examples/crashreport.c @@ -8,6 +8,7 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/log/log.h" +#include "libc/runtime/symbols.internal.h" /** * @fileoverview How to print backtraces and cpu state on crash. diff --git a/examples/examples.mk b/examples/examples.mk index a76e23ebd..127f9e2f5 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -143,14 +143,6 @@ o/$(MODE)/examples/nesemu1.com: \ @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/examples/.nesemu1/.symtab -o/$(MODE)/examples/hello.com.dbg: \ - $(EXAMPLES_DEPS) \ - o/$(MODE)/examples/hello.o \ - o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m $(EXAMPLES_OBJS): examples/examples.mk diff --git a/examples/greenbean.c b/examples/greenbean.c index 29b3c3596..58338fd69 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.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/bits/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" @@ -27,6 +28,7 @@ #include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" +#include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sock/goodsocket.internal.h" @@ -90,7 +92,8 @@ * */ -#define THREADS 512 +#define PORT 8080 +#define THREADS 10000 #define HEARTBEAT 100 #define KEEPALIVE 5000 #define LOGGING 0 @@ -100,120 +103,39 @@ "Referrer-Policy: origin\r\n" \ "Cache-Control: private; max-age=0\r\n" -//////////////////////////////////////////////////////////////////////////////// -// BEGIN: Chris Wellons's Public Domain GNU Atomics Library - -#define BARRIER_INC(x) __atomic_add_fetch(x, 1, __ATOMIC_SEQ_CST) -#define BARRIER_GET(x) __atomic_load_n(x, __ATOMIC_SEQ_CST) -#define ATOMIC_LOAD(q) __atomic_load_n(q, __ATOMIC_ACQUIRE) -#define ATOMIC_RLOAD(q) __atomic_load_n(q, __ATOMIC_RELAXED) -#define ATOMIC_STORE(q, v) __atomic_store_n(q, v, __ATOMIC_RELEASE) -#define ATOMIC_ADD(q, c) __atomic_add_fetch(q, c, __ATOMIC_RELEASE) -#define ATOMIC_AND(q, m) __atomic_and_fetch(q, m, __ATOMIC_RELEASE) -#define ATOMIC_CAS(q, e, d) \ - __atomic_compare_exchange_n(q, e, d, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED) - -// Return the array index for then next value to be pushed. The size of this -// array must be (1 << exp) elements. Write the value into this array index, -// then commit it. With a single-consumer queue, this element store need not -// be atomic. The value will appear in the queue after the commit. Returns -// -1 if the queue is full. -static int queue_push(uint32_t *q, int exp) { - uint32_t r = ATOMIC_LOAD(q); - int mask = (1u << exp) - 1; - int head = r & mask; - int tail = r >> 16 & mask; - int next = (head + 1u) & mask; - if (r & 0x8000) { // avoid overflow on commit - ATOMIC_AND(q, ~0x8000); - } - return next == tail ? -1 : head; -} - -// Commits and completes the push operation. Do this after storing into the -// array. This operation cannot fail. -static void queue_push_commit(uint32_t *q) { - ATOMIC_ADD(q, 1); -} - -// Return the array index for the next value to be popped. The size of this -// array must be (1 << exp) elements. Read from this array index, then -// commit the pop. This element load need not be atomic. The value will be -// removed from the queue after the commit. Returns -1 if the queue is -// empty. -static int queue_pop(uint32_t *q, int exp) { - uint32_t r = ATOMIC_LOAD(q); - int mask = (1u << exp) - 1; - int head = r & mask; - int tail = r >> 16 & mask; - return head == tail ? -1 : tail; -} - -// Commits and completes the pop operation. Do this after loading from the -// array. This operation cannot fail. -static void queue_pop_commit(uint32_t *q) { - ATOMIC_ADD(q, 0x10000); -} - -// Like queue_pop() but for multiple-consumer queues. The element load must -// be atomic since it is concurrent with the producer's push, though it can -// use a relaxed memory order. The loaded value must not be used unless the -// commit is successful. Stores a temporary "save" to be used at commit. -static int queue_mpop(uint32_t *q, int exp, uint32_t *save) { - uint32_t r = *save = ATOMIC_LOAD(q); - int mask = (1u << exp) - 1; - int head = r & mask; - int tail = r >> 16 & mask; - return head == tail ? -1 : tail; -} - -// Like queue_pop_commit() but for multiple-consumer queues. It may fail if -// another consumer pops concurrently, in which case the pop must be retried -// from the beginning. -static bool queue_mpop_commit(uint32_t *q, uint32_t save) { - return ATOMIC_CAS(q, &save, save + 0x10000); -} - -// Spin-lock barrier for n threads, where n is a power of two. -// Initialize *barrier to zero. -static void barrier_waitn(int *barrier, int n) { - int v = BARRIER_INC(barrier); - if (v & (n - 1)) { - for (v &= n; (BARRIER_GET(barrier) & n) == v;) { - donothing; - } - } -} - -// END: Chris Wellons's Public Domain GNU Atomics Library -//////////////////////////////////////////////////////////////////////////////// - -int barrier1; -int itsbegun; +int workers; +int messages; +int connections; int closingtime; -int barrier2; -int itsdone; +const char *status; int Worker(void *id) { int server, yes = 1; - kprintf(" %d", id); - barrier_waitn(&barrier1, THREADS); - itsbegun = true; - // load balance incoming connections for port 8080 across all threads // hangup on any browser clients that lag for more than a few seconds struct timeval timeo = {KEEPALIVE / 1000, KEEPALIVE % 1000}; - struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(8080)}; - CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)}; + + server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (server == -1) { + if (LOGGING) kprintf("%s() failed %m\n", "socket"); + goto WorkerFinished; + } + setsockopt(server, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); setsockopt(server, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)); setsockopt(server, SOL_TCP, TCP_FASTOPEN, &yes, sizeof(yes)); setsockopt(server, SOL_TCP, TCP_QUICKACK, &yes, sizeof(yes)); - CHECK_EQ(0, bind(server, &addr, sizeof(addr))); - CHECK_EQ(0, listen(server, 10)); + + if (bind(server, &addr, sizeof(addr)) == -1) { + if (LOGGING) kprintf("%s() failed %m\n", "socket"); + goto WorkerFinished; + } + + listen(server, 10); // connection loop while (!closingtime) { @@ -228,8 +150,12 @@ int Worker(void *id) { char inbuf[1500], outbuf[512], *p, *q; int clientip, client, inmsglen, outmsglen; + if (!IsLinux() && + poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) { + continue; + } + // wait for client connection - if (poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) continue; clientaddrsize = sizeof(clientaddr); client = accept(server, &clientaddr, &clientaddrsize); @@ -245,6 +171,8 @@ int Worker(void *id) { continue; } + asm volatile("lock incl\t%0" : "+m"(connections)); + // message loop do { // parse the incoming http message @@ -253,6 +181,7 @@ int Worker(void *id) { if ((got = read(client, inbuf, sizeof(inbuf))) <= 0) break; // check that client message wasn't fragmented into more reads if (!(inmsglen = ParseHttpMessage(&msg, inbuf, got))) break; + asm volatile("lock incl\t%0" : "+m"(messages)); #if LOGGING // log the incoming http message @@ -313,35 +242,46 @@ int Worker(void *id) { (msg.method == kHttpGet || msg.method == kHttpHead)); DestroyHttpMessage(&msg); close(client); + asm volatile("lock decl\t%0" : "+m"(connections)); } // inform the parent that this clone has finished +WorkerFinished: close(server); - kprintf(" %d", id); - barrier_waitn(&barrier2, THREADS); - itsdone = true; + asm volatile("lock decl\t%0" : "+m"(workers)); return 0; } void OnCtrlC(int sig) { closingtime = true; + status = " shutting down..."; } int main(int argc, char *argv[]) { - /* ShowCrashReports(); */ - int64_t loadtzdbearly; - kprintf("welcome to greenbean\n"); - gmtime(&loadtzdbearly); - for (int i = 0; i < THREADS; ++i) { + int i; + uint32_t *hostips; + ShowCrashReports(); + sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0); + for (hostips = GetHostIps(), i = 0; hostips[i]; ++i) { + kprintf("listening on http://%d.%d.%d.%d:%d\n", + (hostips[i] & 0xff000000) >> 030, (hostips[i] & 0x00ff0000) >> 020, + (hostips[i] & 0x0000ff00) >> 010, (hostips[i] & 0x000000ff) >> 000, + PORT); + } + workers = THREADS; + for (i = 0; i < THREADS; ++i) { void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); clone(Worker, stack, 65536, CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(intptr_t)i, 0, 0, 0, 0); } - while (!ATOMIC_LOAD(&itsbegun)) usleep(HEARTBEAT * 1000); - sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0); - kprintf("\nit's begun\n"); - while (!ATOMIC_LOAD(&itsdone)) usleep(HEARTBEAT * 1000); - kprintf("\nthank you for flying greenbean\n"); + status = ""; + while (workers) { + kprintf( + "\r\e[K\e[32mgreenbean\e[0m workers=%d connections=%d messages=%d%s ", + workers, connections, messages, status); + usleep(HEARTBEAT * 1000); + } + kprintf("\r\e[K"); } diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index c5cf3aadd..c238eb5c1 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,10 +65,21 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) +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-xnu.o: \ + OVERRIDE_COPTS += \ + -mno-fentry \ + -fno-stack-protector \ + -fno-sanitize=all + +o/$(MODE)/libc/calls/sys_mprotect.greg.o \ o/$(MODE)/libc/calls/vdsofunc.greg.o \ o/$(MODE)/libc/calls/directmap.o \ o/$(MODE)/libc/calls/directmap-nt.o \ o/$(MODE)/libc/calls/mapstack.greg.o \ +o/$(MODE)/libc/calls/execve-nt.greg.o \ o/$(MODE)/libc/calls/getcwd.greg.o \ o/$(MODE)/libc/calls/getcwd-xnu.greg.o \ o/$(MODE)/libc/calls/getprogramexecutablename.greg.o \ @@ -114,7 +125,7 @@ o/$(MODE)/libc/calls/execl.o \ o/$(MODE)/libc/calls/execle.o \ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/copyfile.o \ -o/$(MODE)/libc/calls/execve-nt.o \ +o/$(MODE)/libc/calls/execve-nt.greg.o \ o/$(MODE)/libc/calls/linkat-nt.o \ o/$(MODE)/libc/calls/renameat-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ diff --git a/libc/calls/execve-nt.c b/libc/calls/execve-nt.greg.c similarity index 59% rename from libc/calls/execve-nt.c rename to libc/calls/execve-nt.greg.c index c45128e8c..003a761dd 100644 --- a/libc/calls/execve-nt.c +++ b/libc/calls/execve-nt.greg.c @@ -16,44 +16,95 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#define ShouldUseMsabiAttribute() 1 +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/ntspawn.h" +#include "libc/calls/strace.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/memory.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/startupinfo.h" #include "libc/nt/synchronization.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.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/errfuns.h" + +__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; +__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; +__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; +__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; + +static noinstrument __msabi bool32 +BlockExecveConsoleEvent(uint32_t dwCtrlType) { + // block SIGINT and SIGQUIT in execve() parent process + return true; +} textwindows int sys_execve_nt(const char *program, char *const argv[], char *const envp[]) { int rc; size_t i; uint32_t dwExitCode; + struct MemoryIntervals *mm; struct NtStartupInfo startinfo; struct NtProcessInformation procinfo; + + // this is a non-recoverable operation, so do some manual validation + if (sys_faccessat_nt(AT_FDCWD, program, X_OK, 0) == -1) { + return eacces(); + } + + ////////////////////////////////////////////////////////////////////////////// + // execve operation is unrecoverable from this point + + // close cloexec handles + for (i = 3; i < g_fds.n; ++i) { + if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) { + __imp_CloseHandle(g_fds.p[i].handle); + } + } + bzero(&startinfo, sizeof(startinfo)); startinfo.cb = sizeof(struct NtStartupInfo); startinfo.dwFlags = kNtStartfUsestdhandles; startinfo.hStdInput = __getfdhandleactual(0); startinfo.hStdOutput = __getfdhandleactual(1); startinfo.hStdError = __getfdhandleactual(2); + + // spawn the process rc = ntspawn(program, argv, envp, 0, 0, 0, 1, 0, 0, &startinfo, &procinfo); - if (rc == -1) return -1; - CloseHandle(g_fds.p[0].handle); - CloseHandle(g_fds.p[1].handle); - CloseHandle(procinfo.hThread); + if (rc == -1) { + STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program); + __imp_ExitProcess(6543); + } + + ////////////////////////////////////////////////////////////////////////////// + // zombie shell process remains, to wait for child and propagate its exit + // code + + __imp_CloseHandle(g_fds.p[0].handle); + __imp_CloseHandle(g_fds.p[1].handle); + __imp_CloseHandle(procinfo.hThread); + __imp_SetConsoleCtrlHandler((void *)BlockExecveConsoleEvent, 1); do { - WaitForSingleObject(procinfo.hProcess, -1); + __imp_WaitForSingleObject(procinfo.hProcess, -1); dwExitCode = kNtStillActive; - GetExitCodeProcess(procinfo.hProcess, &dwExitCode); + __imp_GetExitCodeProcess(procinfo.hProcess, &dwExitCode); } while (dwExitCode == kNtStillActive); - CloseHandle(procinfo.hProcess); - _Exit(dwExitCode); + __imp_CloseHandle(procinfo.hProcess); + __imp_ExitProcess(dwExitCode); + unreachable; } diff --git a/libc/calls/execve.c b/libc/calls/execve.c index 9df027a14..ab4ac6ac7 100644 --- a/libc/calls/execve.c +++ b/libc/calls/execve.c @@ -63,11 +63,6 @@ int execve(const char *prog, char *const argv[], char *const envp[]) { kprintf("})\n"); } #endif - for (i = 3; i < g_fds.n; ++i) { - if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) { - close(i); - } - } if (!IsWindows()) { rc = sys_execve(prog, argv, envp); } else { diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 8ec7a418c..e656c947b 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -134,6 +134,7 @@ i32 __sys_pipe2(i32[hasatleast 2], u32) hidden; i32 __sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; i32 __sys_wait4(i32, i32 *, i32, struct rusage *) hidden; +i32 sys_arch_prctl(i32, i64) hidden; i32 sys_chdir(const char *) hidden; i32 sys_chroot(const char *) hidden; i32 sys_clock_gettime(i32, struct timespec *) hidden; diff --git a/libc/calls/ntcontext2linux.c b/libc/calls/ntcontext2linux.c index c1a509b1e..e21204e6f 100644 --- a/libc/calls/ntcontext2linux.c +++ b/libc/calls/ntcontext2linux.c @@ -18,10 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/ucontext.h" +#include "libc/log/libfatal.internal.h" #include "libc/nt/struct/context.h" #include "libc/str/str.h" -textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { +privileged void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { if (!cr) return; ctx->uc_flags = cr->EFlags; ctx->uc_mcontext.eflags = cr->EFlags; @@ -46,10 +47,10 @@ textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { ctx->uc_mcontext.gs = cr->SegGs; ctx->uc_mcontext.fs = cr->SegFs; ctx->uc_mcontext.fpregs = &ctx->__fpustate; - memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate)); + __repmovsb(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate)); } -textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) { +privileged void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) { if (!cr) return; cr->EFlags = ctx->uc_flags; cr->EFlags = ctx->uc_mcontext.eflags; @@ -73,5 +74,5 @@ textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) { cr->SegCs = ctx->uc_mcontext.cs; cr->SegGs = ctx->uc_mcontext.gs; cr->SegFs = ctx->uc_mcontext.fs; - memcpy(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate)); + __repmovsb(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate)); } diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index 45626eff3..7414fd999 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/spinlock.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" @@ -90,8 +91,8 @@ static textwindows struct Signal *__sig_remove(void) { * @note called from main thread * @return true if EINTR should be returned by caller */ -static textwindows bool __sig_deliver(bool restartable, int sig, int si_code, - ucontext_t *ctx) { +static privileged bool __sig_deliver(bool restartable, int sig, int si_code, + ucontext_t *ctx) { unsigned rva, flags; siginfo_t info, *infop; STRACE("delivering %G", sig); @@ -113,7 +114,7 @@ static textwindows bool __sig_deliver(bool restartable, int sig, int si_code, // setup the somewhat expensive information args // only if they're requested by the user in sigaction() if (flags & SA_SIGINFO) { - bzero(&info, sizeof(info)); + __repstosb(&info, 0, sizeof(info)); info.si_signo = sig; info.si_code = si_code; infop = &info; @@ -162,8 +163,8 @@ static textwindows bool __sig_isfatal(int sig) { * @param restartable can be used to suppress true return if SA_RESTART * @return true if signal was delivered */ -textwindows bool __sig_handle(bool restartable, int sig, int si_code, - ucontext_t *ctx) { +privileged bool __sig_handle(bool restartable, int sig, int si_code, + ucontext_t *ctx) { bool delivered; switch (__sighandrvas[sig]) { case (intptr_t)SIG_DFL: diff --git a/libc/calls/sigenter-freebsd.c b/libc/calls/sigenter-freebsd.c index 352d3f5d8..7afbb42d5 100644 --- a/libc/calls/sigenter-freebsd.c +++ b/libc/calls/sigenter-freebsd.c @@ -25,13 +25,13 @@ #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/repstosb.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, - struct ucontext_freebsd *ctx) { +privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, + struct ucontext_freebsd *ctx) { int rva, flags; struct Goodies { ucontext_t uc; @@ -43,14 +43,14 @@ void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, if (~flags & SA_SIGINFO) { ((sigaction_f)(_base + rva))(sig, 0, 0); } else { - repstosb(&g, 0, sizeof(g)); + __repstosb(&g, 0, sizeof(g)); g.uc.uc_mcontext.fpregs = &g.uc.__fpustate; g.uc.uc_stack.ss_sp = ctx->uc_stack.ss_sp; g.uc.uc_stack.ss_size = ctx->uc_stack.ss_size; g.uc.uc_stack.ss_flags = ctx->uc_stack.ss_flags; g.uc.uc_flags = ctx->uc_flags; - memcpy(&g.uc.uc_sigmask, &ctx->uc_sigmask, - MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask))); + __repmovsb(&g.uc.uc_sigmask, &ctx->uc_sigmask, + MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask))); g.uc.uc_mcontext.r8 = ctx->uc_mcontext.mc_r8; g.uc.uc_mcontext.r9 = ctx->uc_mcontext.mc_r9; g.uc.uc_mcontext.r10 = ctx->uc_mcontext.mc_r10; @@ -73,7 +73,7 @@ void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, g.uc.uc_mcontext.gs = ctx->uc_mcontext.mc_gs; g.uc.uc_mcontext.err = ctx->uc_mcontext.mc_err; g.uc.uc_mcontext.trapno = ctx->uc_mcontext.mc_trapno; - memcpy(&g.uc.__fpustate, &ctx->uc_mcontext.mc_fpstate, 512); + __repmovsb(&g.uc.__fpustate, &ctx->uc_mcontext.mc_fpstate, 512); g.si.si_signo = freebsdinfo->si_signo; g.si.si_errno = freebsdinfo->si_errno; g.si.si_code = freebsdinfo->si_code; @@ -89,8 +89,8 @@ void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, ctx->uc_stack.ss_size = g.uc.uc_stack.ss_size; ctx->uc_stack.ss_flags = g.uc.uc_stack.ss_flags; ctx->uc_flags = g.uc.uc_flags; - memcpy(&ctx->uc_sigmask, &g.uc.uc_sigmask, - MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask))); + __repmovsb(&ctx->uc_sigmask, &g.uc.uc_sigmask, + MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask))); ctx->uc_mcontext.mc_rdi = g.uc.uc_mcontext.rdi; ctx->uc_mcontext.mc_rsi = g.uc.uc_mcontext.rsi; ctx->uc_mcontext.mc_rdx = g.uc.uc_mcontext.rdx; @@ -113,7 +113,7 @@ void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo, ctx->uc_mcontext.mc_err = g.uc.uc_mcontext.err; ctx->uc_mcontext.mc_rip = g.uc.uc_mcontext.rip; ctx->uc_mcontext.mc_rsp = g.uc.uc_mcontext.rsp; - memcpy(&ctx->uc_mcontext.mc_fpstate, &g.uc.__fpustate, 512); + __repmovsb(&ctx->uc_mcontext.mc_fpstate, &g.uc.__fpustate, 512); } } /* diff --git a/libc/calls/sigenter-netbsd.c b/libc/calls/sigenter-netbsd.c index 0d2595a96..50a1a93d8 100644 --- a/libc/calls/sigenter-netbsd.c +++ b/libc/calls/sigenter-netbsd.c @@ -24,12 +24,13 @@ #include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, - struct ucontext_netbsd *ctx) { +privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, + struct ucontext_netbsd *ctx) { int rva, flags; ucontext_t uc; struct siginfo si2; @@ -39,8 +40,8 @@ void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, if (~flags & SA_SIGINFO) { ((sigaction_f)(_base + rva))(sig, 0, 0); } else { - bzero(&uc, sizeof(uc)); - bzero(&si2, sizeof(si2)); + __repstosb(&uc, 0, sizeof(uc)); + __repstosb(&si2, 0, sizeof(si2)); si2.si_signo = si->si_signo; si2.si_code = si->si_code; si2.si_errno = si->si_errno; @@ -52,8 +53,8 @@ void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, uc.uc_stack.ss_sp = ctx->uc_stack.ss_sp; uc.uc_stack.ss_size = ctx->uc_stack.ss_size; uc.uc_stack.ss_flags = ctx->uc_stack.ss_flags; - memcpy(&uc.uc_sigmask, &ctx->uc_sigmask, - MIN(sizeof(uc.uc_sigmask), sizeof(ctx->uc_sigmask))); + __repmovsb(&uc.uc_sigmask, &ctx->uc_sigmask, + MIN(sizeof(uc.uc_sigmask), sizeof(ctx->uc_sigmask))); uc.uc_mcontext.rdi = ctx->uc_mcontext.rdi; uc.uc_mcontext.rsi = ctx->uc_mcontext.rsi; uc.uc_mcontext.rdx = ctx->uc_mcontext.rdx; diff --git a/libc/calls/sigenter-openbsd.c b/libc/calls/sigenter-openbsd.c index 78a1dc0aa..253d61f50 100644 --- a/libc/calls/sigenter-openbsd.c +++ b/libc/calls/sigenter-openbsd.c @@ -24,13 +24,13 @@ #include "libc/calls/struct/ucontext-openbsd.internal.h" #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" -#include "libc/intrin/repstosb.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, - struct ucontext_openbsd *ctx) { +privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, + struct ucontext_openbsd *ctx) { int rva, flags; struct Goodies { ucontext_t uc; @@ -42,7 +42,7 @@ void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, if (~flags & SA_SIGINFO) { ((sigaction_f)(_base + rva))(sig, 0, 0); } else { - repstosb(&g, 0, sizeof(g)); + __repstosb(&g, 0, sizeof(g)); g.si.si_signo = openbsdinfo->si_signo; g.si.si_code = openbsdinfo->si_code; g.si.si_errno = openbsdinfo->si_errno; @@ -54,8 +54,8 @@ void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, } g.si.si_value = openbsdinfo->si_value; g.uc.uc_mcontext.fpregs = &g.uc.__fpustate; - memcpy(&g.uc.uc_sigmask, &ctx->sc_mask, - MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->sc_mask))); + __repmovsb(&g.uc.uc_sigmask, &ctx->sc_mask, + MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->sc_mask))); g.uc.uc_mcontext.rdi = ctx->sc_rdi; g.uc.uc_mcontext.rsi = ctx->sc_rsi; g.uc.uc_mcontext.rdx = ctx->sc_rdx; diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index 4065ab3ce..46a2fb831 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -25,6 +25,7 @@ #include "libc/calls/ucontext.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/repstosb.h" +#include "libc/log/libfatal.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -355,19 +356,19 @@ struct __darwin_ucontext { struct __darwin_mcontext64 *uc_mcontext; }; -noasan static void xnuexceptionstate2linux( +static privileged void xnuexceptionstate2linux( mcontext_t *mc, struct __darwin_x86_exception_state64 *xnues) { mc->trapno = xnues->__trapno; mc->err = xnues->__err; } -noasan static void linuxexceptionstate2xnu( +static privileged void linuxexceptionstate2xnu( struct __darwin_x86_exception_state64 *xnues, mcontext_t *mc) { xnues->__trapno = mc->trapno; xnues->__err = mc->err; } -noasan static void xnuthreadstate2linux( +static privileged void xnuthreadstate2linux( mcontext_t *mc, struct __darwin_x86_thread_state64 *xnuss) { mc->rdi = xnuss->__rdi; mc->rsi = xnuss->__rsi; @@ -392,7 +393,7 @@ noasan static void xnuthreadstate2linux( mc->r15 = xnuss->__r15; } -noasan static void linuxthreadstate2xnu( +static privileged void linuxthreadstate2xnu( struct __darwin_x86_thread_state64 *xnuss, ucontext_t *uc, mcontext_t *mc) { xnuss->__rdi = mc->rdi; xnuss->__rsi = mc->rsi; @@ -417,14 +418,14 @@ noasan static void linuxthreadstate2xnu( xnuss->__r15 = mc->r15; } -noasan static void CopyFpXmmRegs(void *d, const void *s) { +static privileged void CopyFpXmmRegs(void *d, const void *s) { size_t i; for (i = 0; i < (8 + 16) * 16; i += 16) { __builtin_memcpy((char *)d + i, (const char *)s + i, 16); } } -noasan static void xnussefpustate2linux( +static privileged void xnussefpustate2linux( struct FpuState *fs, struct __darwin_x86_float_state64 *xnufs) { fs->cwd = xnufs->__fpu_fcw; fs->swd = xnufs->__fpu_fsw; @@ -437,7 +438,7 @@ noasan static void xnussefpustate2linux( CopyFpXmmRegs(fs->st, &xnufs->__fpu_stmm0); } -noasan static void linuxssefpustate2xnu( +static privileged void linuxssefpustate2xnu( struct __darwin_x86_float_state64 *xnufs, struct FpuState *fs) { xnufs->__fpu_fcw = fs->cwd; xnufs->__fpu_fsw = fs->swd; @@ -450,9 +451,9 @@ noasan static void linuxssefpustate2xnu( CopyFpXmmRegs(&xnufs->__fpu_stmm0, fs->st); } -noasan void __sigenter_xnu(void *fn, int infostyle, int sig, - struct siginfo_xnu *xnuinfo, - struct __darwin_ucontext *xnuctx) { +privileged void __sigenter_xnu(void *fn, int infostyle, int sig, + struct siginfo_xnu *xnuinfo, + struct __darwin_ucontext *xnuctx) { intptr_t ax; int rva, flags; struct Goodies { @@ -465,7 +466,7 @@ noasan void __sigenter_xnu(void *fn, int infostyle, int sig, if (~flags & SA_SIGINFO) { ((sigaction_f)(_base + rva))(sig, 0, 0); } else { - repstosb(&g, 0, sizeof(g)); + __repstosb(&g, 0, sizeof(g)); if (xnuctx) { g.uc.uc_flags = xnuctx->uc_onstack ? SA_ONSTACK : 0; g.uc.uc_sigmask.__bits[0] = xnuctx->uc_sigmask; diff --git a/libc/calls/sigprocmask-sysv.c b/libc/calls/sigprocmask-sysv.greg.c similarity index 100% rename from libc/calls/sigprocmask-sysv.c rename to libc/calls/sigprocmask-sysv.greg.c diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index bd2487faf..fbe717a5e 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -30,7 +30,7 @@ #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" -textwindows unsigned __wincrash(struct NtExceptionPointers *ep) { +privileged unsigned __wincrash(struct NtExceptionPointers *ep) { int64_t rip; int sig, code; ucontext_t ctx; diff --git a/libc/errno.h b/libc/errno.h index 75c0e8609..da40ebf9c 100644 --- a/libc/errno.h +++ b/libc/errno.h @@ -8,7 +8,7 @@ COSMOPOLITAN_C_START_ * @see libc/sysv/consts.sh for numbers */ -extern errno_t errno; +#define errno (*__errno_location()) /** * System call unavailable. @@ -686,6 +686,10 @@ extern const long EXFULL; #define EXDEV EXDEV #define EXFULL EXFULL +extern errno_t __errno; + +errno_t *__errno_location(void); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_ERRNO_H_ */ diff --git a/libc/fmt/sprintf.c b/libc/fmt/sprintf.c index 46174ff2c..3cd64c860 100644 --- a/libc/fmt/sprintf.c +++ b/libc/fmt/sprintf.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/log/log.h" /** * Formats string to buffer that's hopefully large enough. diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 50463291d..b3f880840 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -66,6 +66,7 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \ o/$(MODE)/libc/intrin/tls.greg.o \ o/$(MODE)/libc/intrin/exit.greg.o \ +o/$(MODE)/libc/intrin/errno.greg.o \ o/$(MODE)/libc/intrin/exit1.greg.o \ o/$(MODE)/libc/intrin/gettid.greg.o \ o/$(MODE)/libc/intrin/getenv.greg.o \ diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 90f8177c5..1915f8afb 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -262,6 +262,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, continue; case '#': + case '`': hash = '0'; continue; diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index e66e1aec2..ea18cf58d 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -21,7 +21,7 @@ #define _seizelock(lock) \ do { \ typeof(*(lock)) x = 1; \ - __atomic_store(lock, &x, __ATOMIC_SEQ_CST); \ + __atomic_store(lock, &x, __ATOMIC_RELEASE); \ } while (0) #define _spinlock_tiny(lock) \ diff --git a/libc/intrin/threaded.internal.h b/libc/intrin/threaded.internal.h index 7b31c0b8c..0dff79330 100644 --- a/libc/intrin/threaded.internal.h +++ b/libc/intrin/threaded.internal.h @@ -3,6 +3,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +extern bool __hastls; extern bool __threaded; COSMOPOLITAN_C_END_ diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index 2dec7159a..d78503707 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -83,6 +83,9 @@ void *TlsGetValue(uint32_t dwTlsIndex) { : "=r"(lpTlsValue) : "m"(*((long *)0x1480 + dwTlsIndex))); return lpTlsValue; + // // this could also be written as... + // asm("movq\t%%gs:0x30,%0" : "=a"(tib)); + // return (void *)tib[0x1480 / 8 + dwTlsIndex]; } else { return __imp_TlsGetValue(dwTlsIndex); } diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 611d05688..05aee9fa4 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -74,15 +74,18 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { return -1; } - /* - * DWARF is a weak standard. Platforms that use LLVM or old GNU - * usually can't be counted upon to print backtraces correctly. - */ + // DWARF is a weak standard. Platforms that use LLVM or old GNU + // usually can't be counted upon to print backtraces correctly. if (!IsLinux() && !IsWindows()) { ShowHint("won't print addr2line backtrace because probably llvm"); return -1; } + if (IsWindows()) { + // TODO: We need a way to *not* pass //?/C:/... paths to mingw + return -1; + } + i = 0; j = 0; argv[i++] = "addr2line"; diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index b517e2ead..dce6c0fb2 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -277,6 +277,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, intptr_t rip; int gdbpid, err; static bool noreentry, notpossible; + STRACE("__oncrash rip %x", ctx->uc_mcontext.rip); __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); if (_lockcmpxchg(&noreentry, false, true)) { diff --git a/libc/macros.internal.inc b/libc/macros.internal.inc index f4e0de543..15772f719 100644 --- a/libc/macros.internal.inc +++ b/libc/macros.internal.inc @@ -124,6 +124,15 @@ .section .privileged,"ax",@progbits .endm +// Loads address of errno into %rcx +.macro .errno + call __errno_location +// cs +// cs +// cs +// mov $__errno,%eax +.endm + // Post-Initialization Read-Only (PIRO) BSS section. // @param ss is an optional string, for control image locality .macro .piro ss diff --git a/libc/nexgen32e/hastls.c b/libc/nexgen32e/hastls.c new file mode 100644 index 000000000..84fad017e --- /dev/null +++ b/libc/nexgen32e/hastls.c @@ -0,0 +1,21 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/threaded.internal.h" + +bool __hastls; diff --git a/libc/nexgen32e/nexgen32e.mk b/libc/nexgen32e/nexgen32e.mk index d00fbdd9a..5c6df13c2 100644 --- a/libc/nexgen32e/nexgen32e.mk +++ b/libc/nexgen32e/nexgen32e.mk @@ -42,7 +42,8 @@ $(LIBC_NEXGEN32E_A).pkg: \ $(LIBC_NEXGEN32E_A_OBJS) \ $(foreach x,$(LIBC_NEXGEN32E_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/nexgen32e/errno.o: \ +o/$(MODE)/libc/nexgen32e/hastls.o \ +o/$(MODE)/libc/nexgen32e/threaded.o: \ OVERRIDE_CFLAGS += \ $(NO_MAGIC) \ -fno-sanitize=all diff --git a/libc/intrin/threaded.c b/libc/nexgen32e/threaded.c similarity index 100% rename from libc/intrin/threaded.c rename to libc/nexgen32e/threaded.c diff --git a/libc/runtime/arch_prctl.c b/libc/runtime/arch_prctl.c index c5602feb4..f2f454430 100644 --- a/libc/runtime/arch_prctl.c +++ b/libc/runtime/arch_prctl.c @@ -19,6 +19,7 @@ #include "libc/bits/asmflag.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/nexgen32e/msr.h" #include "libc/nexgen32e/x86feature.h" @@ -57,8 +58,6 @@ "d"((uint32_t)(val_ >> 32))); \ } while (0) -int sys_arch_prctl(int, int64_t) hidden; - static inline int arch_prctl_fsgsbase(int code, int64_t addr) { switch (code) { case ARCH_SET_GS: diff --git a/libc/runtime/clone.c b/libc/runtime/clone.greg.c similarity index 87% rename from libc/runtime/clone.c rename to libc/runtime/clone.greg.c index c6295f4e8..990f5960f 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.greg.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/ucontext-netbsd.internal.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" @@ -34,6 +35,7 @@ #include "libc/runtime/runtime.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/nr.h" +#include "libc/sysv/consts/nrlinux.h" #include "libc/sysv/errfuns.h" #include "libc/thread/freebsd.internal.h" #include "libc/thread/xnu.internal.h" @@ -47,10 +49,56 @@ STATIC_YOINK("gettid"); // for kprintf() #define __NR__lwp_setprivate 317 #define __NR_bsdthread_create 0x02000168 #define __NR_thread_fast_set_cthread_self 0x03000003 +#define __NR_sysarch 0x000000a5 +#define __NR___set_tcb 0x00000149 #define PTHREAD_START_CUSTOM_XNU 0x01000000 #define LWP_DETACHED 0x00000040 #define LWP_SUSPENDED 0x00000080 +char __tls[512]; +int __errno_global; +extern int __errno_index; + +privileged void __setup_tls(void) { + int ax, dx; + uint64_t magic; + unsigned char *p; + *(intptr_t *)__tls = (intptr_t)__tls; + *(intptr_t *)(__tls + 0x30) = (intptr_t)__tls; + *(int *)(__tls + 0x3c) = __errno; + if (IsWindows()) { + __errno_index = TlsAlloc(); + TlsSetValue(__errno_index, (void *)(intptr_t)__errno); + } else if (IsLinux()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_linux_arch_prctl), "D"(ARCH_SET_FS), "S"(__tls) + : "rcx", "r11", "memory"); + } else if (IsFreebsd()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_sysarch), "D"(129), "S"(__tls) + : "rcx", "r11", "memory", "cc"); + } else if (IsXnu()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_thread_fast_set_cthread_self), + "D"((intptr_t)__tls - 0x30) + : "rcx", "r11", "memory", "cc"); + } else if (IsOpenbsd()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR___set_tcb), "D"(__tls) + : "rcx", "r11", "memory", "cc"); + } else if (IsNetbsd()) { + asm volatile("syscall" + : "=a"(ax), "=d"(dx) + : "0"(__NR__lwp_setprivate), "D"(__tls) + : "rcx", "r11", "memory", "cc"); + } + __hastls = true; +} + uint32_t WinThreadThunk(void *warg); asm(".section\t.text.windows,\"ax\",@progbits\n\t" ".local\tWinThreadThunk\n" @@ -58,7 +106,8 @@ asm(".section\t.text.windows,\"ax\",@progbits\n\t" "xor\t%ebp,%ebp\n\t" "mov\t%rcx,%rdi\n\t" "mov\t%rcx,%rsp\n\t" - "jmp\tWinThreadMain\n\t" + "and\t$-16,%rsp\n\t" + "call\tWinThreadMain\n\t" ".size\tWinThreadThunk,.-WinThreadThunk\n\t" ".previous"); __attribute__((__used__, __no_reorder__)) @@ -69,7 +118,6 @@ WinThreadMain(struct WinThread *wt) { if (wt->flags & CLONE_CHILD_SETTID) { *wt->ctid = wt->tid; } - // TlsSetValue(__winthread, wt); rc = wt->func(wt->arg); if (wt->flags & CLONE_CHILD_CLEARTID) { *wt->ctid = 0; @@ -380,32 +428,25 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { int ax; - bool failed; - intptr_t *stack; register int *r8 asm("r8") = tls; - register int (*r9)(void *) asm("r9") = func; register int *r10 asm("r10") = ctid; - stack = (intptr_t *)(stk + stksz); - *--stack = (long)arg; // push 1 - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "d"(ptid), - "r"(r10), "r"(r8), "r"(r9) - : "rcx", "r11", "memory"); - if (ax > -4096u) { - errno = -ax; - return -1; - } - if (ax) return ax; - asm volatile("xor\t%%ebp,%%ebp\n\t" - "pop\t%%rdi\n\t" // pop 1 - "call\t*%0\n\t" + register void *r9 asm("r9") = func; + intptr_t *stack = (intptr_t *)(stk + stksz); + *--stack = (intptr_t)arg; + asm volatile("syscall\n\t" + "test\t%0,%0\n\t" + "jnz\t1f\n\t" + "xor\t%%ebp,%%ebp\n\t" + "pop\t%%rdi\n\t" // arg + "call\t*%%r9\n\t" // func "xchg\t%%eax,%%edi\n\t" - "jmp\t_Exit1" - : /* no outputs */ - : "r"(r9) - : "memory"); - unreachable; + "jmp\t_Exit1\n1:" + : "=a"(ax) + : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "r"(r10), + "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + if (ax > -4096u) errno = -ax, ax = -1; + return ax; } /** @@ -433,8 +474,8 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, * if the fork() or vfork() equivalent flags are used it's highly * recommended that this value be GetStackSize(), or else kprintf * and other runtime services providing memory safety can't do as - * good and quick of a job; this value must be 4096-aligned, plus - * it must be at minimum 4096 bytes in size + * good and quick of a job; this value must be 16-aligned plus it + * must be at minimum 4096 bytes in size * @param flags usually has one of * - `SIGCHLD` will delegate to fork() * - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork() @@ -454,15 +495,17 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, * @param tlssz is the size of tls in bytes * @param ctid lets the child receive its thread id; * this parameter is ignored if `CLONE_CHILD_SETTID` is not set - * @return tid on success and 0 to the child, otherwise -1 w/ errno + * @return tid on success and 0 to the child, or -1 w/ errno * @threadsafe */ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { int rc; - // let kprintf() switch from pids to tids __threaded = true; + if (tls && !__hastls) { + __setup_tls(); + } // verify memory is kosher if (IsAsan() && @@ -504,7 +547,7 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, // we now assume we're creating a thread // these platforms can't do signals the way linux does - else if (!IsTiny() && ((stksz < PAGESIZE || (stksz & (PAGESIZE - 1))) || + else if (!IsTiny() && ((stksz < PAGESIZE || (stksz & 15)) || (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) != (CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | @@ -521,15 +564,13 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, } // These platforms can't do segment registers like linux does - else if (flags & CLONE_SETTLS) { - rc = einval(); - } else if (IsWindows()) { + else if (IsWindows()) { rc = CloneWindows(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); } else { rc = enosys(); } - STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d% m", func, stk, + STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d", func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid, rc); return rc; } diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 6f79f9b15..d3a63c7c0 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -89,8 +89,8 @@ privileged noinstrument noasan noubsan void ftracer(void) { frame = __builtin_frame_address(0); frame = frame->next; if (frame->addr != g_lastaddr) { - kprintf("+ %*s%t %d\r\n", GetNestingLevel(frame) * 2, "", frame->addr, - ClocksToNanos(stamp, g_laststamp)); + kprintf("%rFUN %5P %'18T %*s%t\r\n", GetNestingLevel(frame) * 2, "", + frame->addr); g_laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); g_lastaddr = frame->addr; } diff --git a/libc/runtime/getinstructionlengths.c b/libc/runtime/getinstructionlengths.c new file mode 100644 index 000000000..73b57d8a3 --- /dev/null +++ b/libc/runtime/getinstructionlengths.c @@ -0,0 +1,59 @@ +/*-*- 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/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" +#include "third_party/xed/x86.h" + +/** + * Returns lengths of x86 ops in binary. + * + * The first decoded instruction is at `_ereal`. Lengths can be 1 to 15 + * bytes. Each byte in the return value is in that range, and the array + * is NUL terminated. The returned memory is memoized, since this costs + * some time. For example, for a 10mb Python binary, it takes 20 millis + * so the basic idea is is you can use this output multiple times which + * is a faster way to iterate over the binary than calling Xed. + * + * @return nul-terminated length array on success, or null + */ +privileged unsigned char *GetInstructionLengths(void) { + static bool once; + int i, n, err, len, rem; + static unsigned char *res; + struct XedDecodedInst xedd; + unsigned char *p, *mem, *code; + if (!once) { + if ((mem = mmap(0, ROUNDUP(__privileged_addr - _ereal + 1, FRAMESIZE), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0)) != MAP_FAILED) { + for (p = mem, code = _ereal; code < __privileged_addr; code += len) { + rem = __privileged_addr - code; + xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); + err = xed_instruction_length_decode(&xedd, code, rem); + *p++ = len = !err ? xedd.length : 1; + } + res = mem; + } + once = true; + } + return res; +} diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index a6fea7f8d..1e9c5f7b0 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -23,6 +23,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/log/libfatal.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" @@ -56,82 +57,69 @@ privileged noinstrument noasan int __hook(void *ifunc, uint64_t code, mcode; sigset_t mask, oldmask; intptr_t kMcount = (intptr_t)&mcount; - intptr_t kProgramCodeStart = (intptr_t)&_ereal; - intptr_t kPrivilegedStart = (intptr_t)&__privileged_start; - bool kIsBinaryAligned = !(kPrivilegedStart & (PAGESIZE - 1)); - if (!IsWindows()) { - sigfillset(&mask); - sys_sigprocmask(SIG_BLOCK, &mask, &oldmask); - } - if ((rc = mprotect( - (void *)symbols->addr_base, kPrivilegedStart - symbols->addr_base, - kIsBinaryAligned ? PROT_READ | PROT_WRITE - : PROT_READ | PROT_WRITE | PROT_EXEC)) != -1) { - for (i = 0; i < symbols->count; ++i) { - if (symbols->addr_base + symbols->symbols[i].x < kProgramCodeStart) { - continue; - } - if (symbols->addr_base + symbols->symbols[i].y >= kPrivilegedStart) { + intptr_t kProgramCodeStart = (intptr_t)_ereal; + intptr_t kPrivilegedStart = (intptr_t)__privileged_addr; + __morph_begin(); + for (i = 0; i < symbols->count; ++i) { + if (symbols->addr_base + symbols->symbols[i].x < kProgramCodeStart) { + continue; + } + if (symbols->addr_base + symbols->symbols[i].y >= kPrivilegedStart) { + break; + } + for (p = (char *)symbols->addr_base + symbols->symbols[i].x, + pe = (char *)symbols->addr_base + symbols->symbols[i].y; + p + 8 - 1 <= pe; ++p) { + code = ((uint64_t)(255 & p[7]) << 070 | (uint64_t)(255 & p[6]) << 060 | + (uint64_t)(255 & p[5]) << 050 | (uint64_t)(255 & p[4]) << 040 | + (uint64_t)(255 & p[3]) << 030 | (uint64_t)(255 & p[2]) << 020 | + (uint64_t)(255 & p[1]) << 010 | (uint64_t)(255 & p[0]) << 000); + + /* + * Test for -mrecord-mcount (w/ -fpie or -fpic) + * + * nopw 0x00(%rax,%rax,1) ← morphed by package.com + * call *mcount(%rip) ← linked w/o -static + * addr32 call mcount ← relaxed w/ -static + * addr32 call mcount ← relaxed w/ -static + * + * Note that gcc refuses to insert the six byte nop. + */ + if ((code & 0x0000FFFFFFFFFFFF) == 0x0000441F0F66 || + (code & 0x0000FFFFFFFFFFFF) == + ((((kMcount - ((intptr_t)&p[2] + 4)) << 16) | 0xE867) & + 0x0000FFFFFFFFFFFF) || + (code & 0x0000FFFFFFFFFFFF) == + ((((kMcount - ((intptr_t)&p[2] + 4)) << 16) | 0xFF15) & + 0x0000FFFFFFFFFFFF)) { + p[0] = 0x67; + p[1] = 0xE8; + addr = (intptr_t)ifunc - ((intptr_t)&p[2] + 4); + p[2] = (addr & 0x000000ff) >> 000; + p[3] = (addr & 0x0000ff00) >> 010; + p[4] = (addr & 0x00ff0000) >> 020; + p[5] = (addr & 0xff000000) >> 030; break; } - for (p = (char *)symbols->addr_base + symbols->symbols[i].x, - pe = (char *)symbols->addr_base + symbols->symbols[i].y; - p + 8 - 1 <= pe; ++p) { - code = ((uint64_t)(255 & p[7]) << 070 | (uint64_t)(255 & p[6]) << 060 | - (uint64_t)(255 & p[5]) << 050 | (uint64_t)(255 & p[4]) << 040 | - (uint64_t)(255 & p[3]) << 030 | (uint64_t)(255 & p[2]) << 020 | - (uint64_t)(255 & p[1]) << 010 | (uint64_t)(255 & p[0]) << 000); - /* - * Test for -mrecord-mcount (w/ -fpie or -fpic) - * - * nopw 0x00(%rax,%rax,1) ← morphed by package.com - * call *mcount(%rip) ← linked w/o -static - * addr32 call mcount ← relaxed w/ -static - * addr32 call mcount ← relaxed w/ -static - * - * Note that gcc refuses to insert the six byte nop. - */ - if ((code & 0x0000FFFFFFFFFFFF) == 0x0000441F0F66 || - (code & 0x0000FFFFFFFFFFFF) == - ((((kMcount - ((intptr_t)&p[2] + 4)) << 16) | 0xE867) & - 0x0000FFFFFFFFFFFF) || - (code & 0x0000FFFFFFFFFFFF) == - ((((kMcount - ((intptr_t)&p[2] + 4)) << 16) | 0xFF15) & - 0x0000FFFFFFFFFFFF)) { - p[0] = 0x67; - p[1] = 0xE8; - addr = (intptr_t)ifunc - ((intptr_t)&p[2] + 4); - p[2] = (addr & 0x000000ff) >> 000; - p[3] = (addr & 0x0000ff00) >> 010; - p[4] = (addr & 0x00ff0000) >> 020; - p[5] = (addr & 0xff000000) >> 030; - break; - } - - /* - * Test for -mnop-mcount (w/ -fno-pie) - */ - mcode = code & 0x000000FFFFFFFFFF; - if ((mcode == 0x00441F0F /* nopl 0x00(%eax,%eax,1) [canonical] */) || - (mcode == 0x00041F0F67 /* nopl (%eax,%eax,1) [older gcc] */)) { - if (p[-1] != 0x66 /* nopw 0x0(%rax,%rax,1) [donotwant] */) { - p[0] = 0xE8 /* call Jvds */; - addr = (intptr_t)ifunc - ((intptr_t)&p[1] + 4); - p[1] = (addr & 0x000000ff) >> 000; - p[2] = (addr & 0x0000ff00) >> 010; - p[3] = (addr & 0x00ff0000) >> 020; - p[4] = (addr & 0xff000000) >> 030; - } - break; + /* + * Test for -mnop-mcount (w/ -fno-pie) + */ + mcode = code & 0x000000FFFFFFFFFF; + if ((mcode == 0x00441F0F /* nopl 0x00(%eax,%eax,1) [canonical] */) || + (mcode == 0x00041F0F67 /* nopl (%eax,%eax,1) [older gcc] */)) { + if (p[-1] != 0x66 /* nopw 0x0(%rax,%rax,1) [donotwant] */) { + p[0] = 0xE8 /* call Jvds */; + addr = (intptr_t)ifunc - ((intptr_t)&p[1] + 4); + p[1] = (addr & 0x000000ff) >> 000; + p[2] = (addr & 0x0000ff00) >> 010; + p[3] = (addr & 0x00ff0000) >> 020; + p[4] = (addr & 0xff000000) >> 030; } + break; } } - mprotect((void *)symbols->addr_base, kPrivilegedStart - symbols->addr_base, - PROT_READ | PROT_EXEC); } - if (!IsWindows()) { - sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); - } - return rc; + __morph_end(); + return 0; } diff --git a/libc/runtime/morph.greg.c b/libc/runtime/morph.greg.c new file mode 100644 index 000000000..1f11a7f31 --- /dev/null +++ b/libc/runtime/morph.greg.c @@ -0,0 +1,79 @@ +/*-*- 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#define ShouldUseMsabiAttribute() 1 +#include "libc/bits/asmflag.h" +#include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigset.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/nt/enum/pageflags.h" +#include "libc/nt/memory.h" +#include "libc/nt/runtime.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/nr.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sig.h" + +__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; + +static int64_t vector; +static sigset_t oldss; + +static privileged void __morph_mprotect(void *addr, size_t size, int prot, + int ntprot) { + int ax, dx; + uint32_t op; + if (!IsWindows()) { + asm volatile("syscall" + : "=a"(ax), "=d"(dx) + : "0"(__NR_mprotect), "D"(addr), "S"(size), "1"(prot) + : "rcx", "r11", "memory"); + } else { + __imp_VirtualProtect(addr, size, ntprot, &op); + } +} + +/** + * Begins code morphing execuatble. + * + * @return 0 on success, or -1 w/ errno + */ +privileged void __morph_begin(void) { + sigset_t ss; + if (!IsWindows()) { + sigfillset(&ss); + sys_sigprocmask(SIG_BLOCK, &ss, &oldss); + } + __morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_WRITE, + kNtPageWritecopy); +} + +/** + * Begins code morphing execuatble. + */ +privileged void __morph_end(void) { + __morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_EXEC, + kNtPageExecuteRead); + if (!IsWindows()) { + sys_sigprocmask(SIG_SETMASK, &oldss, 0); + } +} diff --git a/libc/runtime/mprotect.greg.c b/libc/runtime/mprotect.greg.c index b15e36c76..ad024e18a 100644 --- a/libc/runtime/mprotect.greg.c +++ b/libc/runtime/mprotect.greg.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/runtime/internal.h" @@ -35,15 +36,17 @@ * @return 0 on success, or -1 w/ errno * @see mmap() */ -privileged int mprotect(void *addr, size_t size, int prot) { +int mprotect(void *addr, size_t size, int prot) { int64_t rc; if (SupportsWindows() && (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GROWSDOWN | PROT_GROWSUP))) { - rc = einval(); // unix checks prot before checking size + errno = EINVAL; // unix checks prot before checking size + rc = -1; } else if (!size) { return 0; // make new technology consistent with unix } else if (UNLIKELY((intptr_t)addr & 4095)) { - rc = einval(); + errno = EINVAL; // unix checks prot before checking size + rc = -1; } else if (!IsWindows()) { rc = sys_mprotect(addr, size, prot); } else { diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 989c6b2de..e813ad377 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -28,10 +28,12 @@ extern unsigned char _etext[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _edata[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _ezip[]; /* αpε */ extern unsigned char _end[] forcealign(FRAMESIZE); /* αpε */ -extern unsigned char _ereal; /* αpε */ -extern unsigned char __privileged_start; /* αpε */ -extern unsigned char __test_start; /* αpε */ -extern unsigned char __ro; /* αpε */ +extern unsigned char _ereal[]; /* αpε */ +extern unsigned char __privileged_start[]; /* αpε */ +extern unsigned char __privileged_addr[]; /* αpε */ +extern unsigned char __privileged_size[]; /* αpε */ +extern unsigned char __test_start[]; /* αpε */ +extern unsigned char __ro[]; /* αpε */ extern unsigned char *__relo_start[]; /* αpε */ extern unsigned char *__relo_end[]; /* αpε */ extern uint8_t __zip_start[]; /* αpε */ @@ -105,6 +107,10 @@ char *GetInterpreterExecutableName(char *, size_t); void __printargs(const char *); void __paginate(int, const char *); int __arg_max(void); +void __morph_begin(void); +void __morph_end(void); +unsigned char *GetFirstInstruction(void); +unsigned char *GetInstructionLengths(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 1892b2408..d7d49bf55 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -43,7 +43,8 @@ LIBC_RUNTIME_A_DIRECTDEPS = \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ - LIBC_SYSV_CALLS + LIBC_SYSV_CALLS \ + THIRD_PARTY_XED LIBC_RUNTIME_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_RUNTIME_A_DIRECTDEPS),$($(x)))) @@ -64,11 +65,13 @@ o/$(MODE)/libc/runtime/directmap.o \ o/$(MODE)/libc/runtime/directmapnt.o \ o/$(MODE)/libc/runtime/findmemoryinterval.o \ o/$(MODE)/libc/runtime/ftrace.greg.o \ +o/$(MODE)/libc/runtime/sys_mprotect.greg.o \ o/$(MODE)/libc/runtime/ftracer.o \ o/$(MODE)/libc/runtime/ezmap.o \ o/$(MODE)/libc/runtime/getdosargv.o \ o/$(MODE)/libc/runtime/getdosenviron.o \ o/$(MODE)/libc/runtime/hook.greg.o \ +o/$(MODE)/libc/runtime/morph.greg.o \ o/$(MODE)/libc/runtime/mprotect.greg.o \ o/$(MODE)/libc/runtime/mprotect-nt.greg.o \ o/$(MODE)/libc/runtime/ismemtracked.greg.o \ @@ -85,6 +88,7 @@ o/$(MODE)/libc/runtime/winmain.greg.o \ o/$(MODE)/libc/runtime/opensymboltable.o \ o/$(MODE)/libc/runtime/getsymboltable.greg.o: \ OVERRIDE_CFLAGS += \ + -Os \ -ffreestanding \ $(NO_MAGIC) diff --git a/libc/runtime/vfork.S b/libc/runtime/vfork.S index b09a0ff36..8d8030ee5 100644 --- a/libc/runtime/vfork.S +++ b/libc/runtime/vfork.S @@ -53,7 +53,7 @@ vfork: call __stracef #endif /* SYSDEBUG */ mov __NR_vfork(%rip),%eax - mov errno(%rip),%r8d # avoid question of @vforksafe errno + mov __errno(%rip),%r8d # avoid question of @vforksafe errno pop %rsi # saves return address in a register #if SupportsBsd() testb IsBsd() @@ -65,7 +65,7 @@ vfork: cmp $-4095,%eax jae systemfive_error #endif -0: mov %r8d,errno(%rip) +0: mov %r8d,__errno(%rip) ezlea __vforked,di test %eax,%eax jz 1f diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 4d737dffa..36c54d12c 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -216,7 +216,7 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { _mmi.p[0].x = allocaddr >> 16; _mmi.p[0].y = (allocaddr >> 16) + ((allocsize >> 16) - 1); _mmi.p[0].prot = prot; - _mmi.p[0].flags = MAP_PRIVATE | MAP_ANONYMOUS; + _mmi.p[0].flags = 0x00000026; // stack+anonymous _mmi.p[0].size = allocsize; _mmi.i = 1; wa = (struct WinArgs *)allocaddr; diff --git a/libc/stubs/ld.S b/libc/stubs/ld.S index 195e33c71..c7e935a5d 100644 --- a/libc/stubs/ld.S +++ b/libc/stubs/ld.S @@ -31,6 +31,8 @@ _ehead = 0 _ereal = 0 __privileged_start = 0 + __privileged_addr = 0 + __privileged_size = 0 __test_start = 0 __ro = 0 __relo_start = 0 @@ -51,6 +53,8 @@ .globl ape_xlm .globl __relo_start .globl __relo_end + .globl __privileged_size + .globl __privileged_addr .globl __privileged_start .globl __ro .globl __test_start @@ -72,6 +76,8 @@ .weak ape_xlm .weak __relo_start .weak __relo_end + .weak __privileged_size + .weak __privileged_addr .weak __privileged_start .weak __ro .weak __test_start diff --git a/libc/sysv/errfuns/e2big.S b/libc/sysv/errfuns/e2big.S index a68faf687..3dee651a0 100644 --- a/libc/sysv/errfuns/e2big.S +++ b/libc/sysv/errfuns/e2big.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + e2big: .leafprologue - .profilable - mov E2BIG(%rip),%eax - mov %eax,errno(%rip) + mov E2BIG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eacces.S b/libc/sysv/errfuns/eacces.S index 4c9bdab5c..f0c068a4a 100644 --- a/libc/sysv/errfuns/eacces.S +++ b/libc/sysv/errfuns/eacces.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eacces: .leafprologue - .profilable - mov EACCES(%rip),%eax - mov %eax,errno(%rip) + mov EACCES(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eaddrinuse.S b/libc/sysv/errfuns/eaddrinuse.S index d6085da6c..88ed82ddf 100644 --- a/libc/sysv/errfuns/eaddrinuse.S +++ b/libc/sysv/errfuns/eaddrinuse.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eaddrinuse: .leafprologue - .profilable - mov EADDRINUSE(%rip),%eax - mov %eax,errno(%rip) + mov EADDRINUSE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eaddrnotavail.S b/libc/sysv/errfuns/eaddrnotavail.S index 590893053..e5d7cd459 100644 --- a/libc/sysv/errfuns/eaddrnotavail.S +++ b/libc/sysv/errfuns/eaddrnotavail.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eaddrnotavail: .leafprologue - .profilable - mov EADDRNOTAVAIL(%rip),%eax - mov %eax,errno(%rip) + mov EADDRNOTAVAIL(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eadv.S b/libc/sysv/errfuns/eadv.S index a29be6ad8..2ec5fad55 100644 --- a/libc/sysv/errfuns/eadv.S +++ b/libc/sysv/errfuns/eadv.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eadv: .leafprologue - .profilable - mov EADV(%rip),%eax - mov %eax,errno(%rip) + mov EADV(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eafnosupport.S b/libc/sysv/errfuns/eafnosupport.S index 8eb3fd1e6..bb61fdf6e 100644 --- a/libc/sysv/errfuns/eafnosupport.S +++ b/libc/sysv/errfuns/eafnosupport.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eafnosupport: .leafprologue - .profilable - mov EAFNOSUPPORT(%rip),%eax - mov %eax,errno(%rip) + mov EAFNOSUPPORT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eagain.S b/libc/sysv/errfuns/eagain.S index 28e7464c1..83ae5326a 100644 --- a/libc/sysv/errfuns/eagain.S +++ b/libc/sysv/errfuns/eagain.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eagain: .leafprologue - .profilable - mov EAGAIN(%rip),%eax - mov %eax,errno(%rip) + mov EAGAIN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ealready.S b/libc/sysv/errfuns/ealready.S index 34921de3d..901053e4c 100644 --- a/libc/sysv/errfuns/ealready.S +++ b/libc/sysv/errfuns/ealready.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ealready: .leafprologue - .profilable - mov EALREADY(%rip),%eax - mov %eax,errno(%rip) + mov EALREADY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebade.S b/libc/sysv/errfuns/ebade.S index d736ef2a0..bba37cbff 100644 --- a/libc/sysv/errfuns/ebade.S +++ b/libc/sysv/errfuns/ebade.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebade: .leafprologue - .profilable - mov EBADE(%rip),%eax - mov %eax,errno(%rip) + mov EBADE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadf.S b/libc/sysv/errfuns/ebadf.S index b07b871e6..faf167f73 100644 --- a/libc/sysv/errfuns/ebadf.S +++ b/libc/sysv/errfuns/ebadf.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadf: .leafprologue - .profilable - mov EBADF(%rip),%eax - mov %eax,errno(%rip) + mov EBADF(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadfd.S b/libc/sysv/errfuns/ebadfd.S index f81b9725e..8b05e7210 100644 --- a/libc/sysv/errfuns/ebadfd.S +++ b/libc/sysv/errfuns/ebadfd.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadfd: .leafprologue - .profilable - mov EBADFD(%rip),%eax - mov %eax,errno(%rip) + mov EBADFD(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadmsg.S b/libc/sysv/errfuns/ebadmsg.S index 8d95e7381..5a4560b5e 100644 --- a/libc/sysv/errfuns/ebadmsg.S +++ b/libc/sysv/errfuns/ebadmsg.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadmsg: .leafprologue - .profilable - mov EBADMSG(%rip),%eax - mov %eax,errno(%rip) + mov EBADMSG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadr.S b/libc/sysv/errfuns/ebadr.S index 69cbfbbe6..7822e2997 100644 --- a/libc/sysv/errfuns/ebadr.S +++ b/libc/sysv/errfuns/ebadr.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadr: .leafprologue - .profilable - mov EBADR(%rip),%eax - mov %eax,errno(%rip) + mov EBADR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadrqc.S b/libc/sysv/errfuns/ebadrqc.S index 0a955024a..4ebe49fc0 100644 --- a/libc/sysv/errfuns/ebadrqc.S +++ b/libc/sysv/errfuns/ebadrqc.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadrqc: .leafprologue - .profilable - mov EBADRQC(%rip),%eax - mov %eax,errno(%rip) + mov EBADRQC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebadslt.S b/libc/sysv/errfuns/ebadslt.S index a32b67f81..389f326b3 100644 --- a/libc/sysv/errfuns/ebadslt.S +++ b/libc/sysv/errfuns/ebadslt.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebadslt: .leafprologue - .profilable - mov EBADSLT(%rip),%eax - mov %eax,errno(%rip) + mov EBADSLT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ebusy.S b/libc/sysv/errfuns/ebusy.S index 064ef7fa5..0d10fbe26 100644 --- a/libc/sysv/errfuns/ebusy.S +++ b/libc/sysv/errfuns/ebusy.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ebusy: .leafprologue - .profilable - mov EBUSY(%rip),%eax - mov %eax,errno(%rip) + mov EBUSY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ecanceled.S b/libc/sysv/errfuns/ecanceled.S index cfacf15ed..c1182c9c2 100644 --- a/libc/sysv/errfuns/ecanceled.S +++ b/libc/sysv/errfuns/ecanceled.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ecanceled: .leafprologue - .profilable - mov ECANCELED(%rip),%eax - mov %eax,errno(%rip) + mov ECANCELED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/echild.S b/libc/sysv/errfuns/echild.S index a276788e9..fb8905559 100644 --- a/libc/sysv/errfuns/echild.S +++ b/libc/sysv/errfuns/echild.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + echild: .leafprologue - .profilable - mov ECHILD(%rip),%eax - mov %eax,errno(%rip) + mov ECHILD(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/echrng.S b/libc/sysv/errfuns/echrng.S index ef21edf0e..272ca1fc5 100644 --- a/libc/sysv/errfuns/echrng.S +++ b/libc/sysv/errfuns/echrng.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + echrng: .leafprologue - .profilable - mov ECHRNG(%rip),%eax - mov %eax,errno(%rip) + mov ECHRNG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ecomm.S b/libc/sysv/errfuns/ecomm.S index 18d5f0f45..159a3057b 100644 --- a/libc/sysv/errfuns/ecomm.S +++ b/libc/sysv/errfuns/ecomm.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ecomm: .leafprologue - .profilable - mov ECOMM(%rip),%eax - mov %eax,errno(%rip) + mov ECOMM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/econnaborted.S b/libc/sysv/errfuns/econnaborted.S index 40bce5cf8..a3af8c15d 100644 --- a/libc/sysv/errfuns/econnaborted.S +++ b/libc/sysv/errfuns/econnaborted.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + econnaborted: .leafprologue - .profilable - mov ECONNABORTED(%rip),%eax - mov %eax,errno(%rip) + mov ECONNABORTED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/econnrefused.S b/libc/sysv/errfuns/econnrefused.S index f63ff40ae..293e09244 100644 --- a/libc/sysv/errfuns/econnrefused.S +++ b/libc/sysv/errfuns/econnrefused.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + econnrefused: .leafprologue - .profilable - mov ECONNREFUSED(%rip),%eax - mov %eax,errno(%rip) + mov ECONNREFUSED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/econnreset.S b/libc/sysv/errfuns/econnreset.S index 41916df81..da02e7656 100644 --- a/libc/sysv/errfuns/econnreset.S +++ b/libc/sysv/errfuns/econnreset.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + econnreset: .leafprologue - .profilable - mov ECONNRESET(%rip),%eax - mov %eax,errno(%rip) + mov ECONNRESET(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/edeadlk.S b/libc/sysv/errfuns/edeadlk.S index fac28d473..e38afcad5 100644 --- a/libc/sysv/errfuns/edeadlk.S +++ b/libc/sysv/errfuns/edeadlk.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + edeadlk: .leafprologue - .profilable - mov EDEADLK(%rip),%eax - mov %eax,errno(%rip) + mov EDEADLK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/edestaddrreq.S b/libc/sysv/errfuns/edestaddrreq.S index d395ecded..01db20eca 100644 --- a/libc/sysv/errfuns/edestaddrreq.S +++ b/libc/sysv/errfuns/edestaddrreq.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + edestaddrreq: .leafprologue - .profilable - mov EDESTADDRREQ(%rip),%eax - mov %eax,errno(%rip) + mov EDESTADDRREQ(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/edom.S b/libc/sysv/errfuns/edom.S index 2981c5002..97fdd0351 100644 --- a/libc/sysv/errfuns/edom.S +++ b/libc/sysv/errfuns/edom.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + edom: .leafprologue - .profilable - mov EDOM(%rip),%eax - mov %eax,errno(%rip) + mov EDOM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/edotdot.S b/libc/sysv/errfuns/edotdot.S index 8995b7b27..cf9188238 100644 --- a/libc/sysv/errfuns/edotdot.S +++ b/libc/sysv/errfuns/edotdot.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + edotdot: .leafprologue - .profilable - mov EDOTDOT(%rip),%eax - mov %eax,errno(%rip) + mov EDOTDOT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/edquot.S b/libc/sysv/errfuns/edquot.S index f8952ead0..f41ed9d9a 100644 --- a/libc/sysv/errfuns/edquot.S +++ b/libc/sysv/errfuns/edquot.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + edquot: .leafprologue - .profilable - mov EDQUOT(%rip),%eax - mov %eax,errno(%rip) + mov EDQUOT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eexist.S b/libc/sysv/errfuns/eexist.S index 63d376c65..eb33d9a4a 100644 --- a/libc/sysv/errfuns/eexist.S +++ b/libc/sysv/errfuns/eexist.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eexist: .leafprologue - .profilable - mov EEXIST(%rip),%eax - mov %eax,errno(%rip) + mov EEXIST(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/efault.S b/libc/sysv/errfuns/efault.S index a96bb91bb..6f21320b0 100644 --- a/libc/sysv/errfuns/efault.S +++ b/libc/sysv/errfuns/efault.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + efault: .leafprologue - .profilable - mov EFAULT(%rip),%eax - mov %eax,errno(%rip) + mov EFAULT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/efbig.S b/libc/sysv/errfuns/efbig.S index b54c708e1..1c445d2e0 100644 --- a/libc/sysv/errfuns/efbig.S +++ b/libc/sysv/errfuns/efbig.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + efbig: .leafprologue - .profilable - mov EFBIG(%rip),%eax - mov %eax,errno(%rip) + mov EFBIG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ehostdown.S b/libc/sysv/errfuns/ehostdown.S index 353efb7d3..a33bf3d23 100644 --- a/libc/sysv/errfuns/ehostdown.S +++ b/libc/sysv/errfuns/ehostdown.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ehostdown: .leafprologue - .profilable - mov EHOSTDOWN(%rip),%eax - mov %eax,errno(%rip) + mov EHOSTDOWN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ehostunreach.S b/libc/sysv/errfuns/ehostunreach.S index 822ebf203..9de92d917 100644 --- a/libc/sysv/errfuns/ehostunreach.S +++ b/libc/sysv/errfuns/ehostunreach.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ehostunreach: .leafprologue - .profilable - mov EHOSTUNREACH(%rip),%eax - mov %eax,errno(%rip) + mov EHOSTUNREACH(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ehwpoison.S b/libc/sysv/errfuns/ehwpoison.S index 4a802b07f..24103a255 100644 --- a/libc/sysv/errfuns/ehwpoison.S +++ b/libc/sysv/errfuns/ehwpoison.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ehwpoison: .leafprologue - .profilable - mov EHWPOISON(%rip),%eax - mov %eax,errno(%rip) + mov EHWPOISON(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eidrm.S b/libc/sysv/errfuns/eidrm.S index 7d8559b39..8b90827df 100644 --- a/libc/sysv/errfuns/eidrm.S +++ b/libc/sysv/errfuns/eidrm.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eidrm: .leafprologue - .profilable - mov EIDRM(%rip),%eax - mov %eax,errno(%rip) + mov EIDRM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eilseq.S b/libc/sysv/errfuns/eilseq.S index ce61f2d5e..689becc06 100644 --- a/libc/sysv/errfuns/eilseq.S +++ b/libc/sysv/errfuns/eilseq.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eilseq: .leafprologue - .profilable - mov EILSEQ(%rip),%eax - mov %eax,errno(%rip) + mov EILSEQ(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/einprogress.S b/libc/sysv/errfuns/einprogress.S index 382cfdacd..b0d7549c6 100644 --- a/libc/sysv/errfuns/einprogress.S +++ b/libc/sysv/errfuns/einprogress.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + einprogress: .leafprologue - .profilable - mov EINPROGRESS(%rip),%eax - mov %eax,errno(%rip) + mov EINPROGRESS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eintr.S b/libc/sysv/errfuns/eintr.S index cc85d0861..9836589f8 100644 --- a/libc/sysv/errfuns/eintr.S +++ b/libc/sysv/errfuns/eintr.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eintr: .leafprologue - .profilable - mov EINTR(%rip),%eax - mov %eax,errno(%rip) + mov EINTR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/einval.S b/libc/sysv/errfuns/einval.S index 76c4c3cfa..83eb007d3 100644 --- a/libc/sysv/errfuns/einval.S +++ b/libc/sysv/errfuns/einval.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + einval: .leafprologue - .profilable - mov EINVAL(%rip),%eax - mov %eax,errno(%rip) + mov EINVAL(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eio.S b/libc/sysv/errfuns/eio.S index b1c6039f1..fcedc61d1 100644 --- a/libc/sysv/errfuns/eio.S +++ b/libc/sysv/errfuns/eio.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eio: .leafprologue - .profilable - mov EIO(%rip),%eax - mov %eax,errno(%rip) + mov EIO(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eisconn.S b/libc/sysv/errfuns/eisconn.S index 59ecc99fd..65ba958cf 100644 --- a/libc/sysv/errfuns/eisconn.S +++ b/libc/sysv/errfuns/eisconn.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eisconn: .leafprologue - .profilable - mov EISCONN(%rip),%eax - mov %eax,errno(%rip) + mov EISCONN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eisdir.S b/libc/sysv/errfuns/eisdir.S index 6b98de9c1..514e60049 100644 --- a/libc/sysv/errfuns/eisdir.S +++ b/libc/sysv/errfuns/eisdir.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eisdir: .leafprologue - .profilable - mov EISDIR(%rip),%eax - mov %eax,errno(%rip) + mov EISDIR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eisnam.S b/libc/sysv/errfuns/eisnam.S index 228efd6b6..b4d5af446 100644 --- a/libc/sysv/errfuns/eisnam.S +++ b/libc/sysv/errfuns/eisnam.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eisnam: .leafprologue - .profilable - mov EISNAM(%rip),%eax - mov %eax,errno(%rip) + mov EISNAM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ekeyexpired.S b/libc/sysv/errfuns/ekeyexpired.S index f7c5eaf1d..4aa6dae18 100644 --- a/libc/sysv/errfuns/ekeyexpired.S +++ b/libc/sysv/errfuns/ekeyexpired.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ekeyexpired: .leafprologue - .profilable - mov EKEYEXPIRED(%rip),%eax - mov %eax,errno(%rip) + mov EKEYEXPIRED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ekeyrejected.S b/libc/sysv/errfuns/ekeyrejected.S index e2fa25a9d..92f289082 100644 --- a/libc/sysv/errfuns/ekeyrejected.S +++ b/libc/sysv/errfuns/ekeyrejected.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ekeyrejected: .leafprologue - .profilable - mov EKEYREJECTED(%rip),%eax - mov %eax,errno(%rip) + mov EKEYREJECTED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/ekeyrevoked.S b/libc/sysv/errfuns/ekeyrevoked.S index 86a6e1dfa..4d2afd375 100644 --- a/libc/sysv/errfuns/ekeyrevoked.S +++ b/libc/sysv/errfuns/ekeyrevoked.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + ekeyrevoked: .leafprologue - .profilable - mov EKEYREVOKED(%rip),%eax - mov %eax,errno(%rip) + mov EKEYREVOKED(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/el2hlt.S b/libc/sysv/errfuns/el2hlt.S index 73e8d895e..e804afa80 100644 --- a/libc/sysv/errfuns/el2hlt.S +++ b/libc/sysv/errfuns/el2hlt.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + el2hlt: .leafprologue - .profilable - mov EL2HLT(%rip),%eax - mov %eax,errno(%rip) + mov EL2HLT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/el2nsync.S b/libc/sysv/errfuns/el2nsync.S index dcf62b85e..a48b7b518 100644 --- a/libc/sysv/errfuns/el2nsync.S +++ b/libc/sysv/errfuns/el2nsync.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + el2nsync: .leafprologue - .profilable - mov EL2NSYNC(%rip),%eax - mov %eax,errno(%rip) + mov EL2NSYNC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/el3hlt.S b/libc/sysv/errfuns/el3hlt.S index 103ab8ddb..0650f2e7d 100644 --- a/libc/sysv/errfuns/el3hlt.S +++ b/libc/sysv/errfuns/el3hlt.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + el3hlt: .leafprologue - .profilable - mov EL3HLT(%rip),%eax - mov %eax,errno(%rip) + mov EL3HLT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/el3rst.S b/libc/sysv/errfuns/el3rst.S index 7e51df535..a0e55575a 100644 --- a/libc/sysv/errfuns/el3rst.S +++ b/libc/sysv/errfuns/el3rst.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + el3rst: .leafprologue - .profilable - mov EL3RST(%rip),%eax - mov %eax,errno(%rip) + mov EL3RST(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elibacc.S b/libc/sysv/errfuns/elibacc.S index d17542dce..efbd75f3c 100644 --- a/libc/sysv/errfuns/elibacc.S +++ b/libc/sysv/errfuns/elibacc.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elibacc: .leafprologue - .profilable - mov ELIBACC(%rip),%eax - mov %eax,errno(%rip) + mov ELIBACC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elibbad.S b/libc/sysv/errfuns/elibbad.S index 8abe0bb51..19a7b596c 100644 --- a/libc/sysv/errfuns/elibbad.S +++ b/libc/sysv/errfuns/elibbad.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elibbad: .leafprologue - .profilable - mov ELIBBAD(%rip),%eax - mov %eax,errno(%rip) + mov ELIBBAD(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elibexec.S b/libc/sysv/errfuns/elibexec.S index f28be8fdd..4e28fa32b 100644 --- a/libc/sysv/errfuns/elibexec.S +++ b/libc/sysv/errfuns/elibexec.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elibexec: .leafprologue - .profilable - mov ELIBEXEC(%rip),%eax - mov %eax,errno(%rip) + mov ELIBEXEC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elibmax.S b/libc/sysv/errfuns/elibmax.S index 607093b3a..b14515535 100644 --- a/libc/sysv/errfuns/elibmax.S +++ b/libc/sysv/errfuns/elibmax.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elibmax: .leafprologue - .profilable - mov ELIBMAX(%rip),%eax - mov %eax,errno(%rip) + mov ELIBMAX(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elibscn.S b/libc/sysv/errfuns/elibscn.S index c1b28eeb3..4781f239d 100644 --- a/libc/sysv/errfuns/elibscn.S +++ b/libc/sysv/errfuns/elibscn.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elibscn: .leafprologue - .profilable - mov ELIBSCN(%rip),%eax - mov %eax,errno(%rip) + mov ELIBSCN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/elnrng.S b/libc/sysv/errfuns/elnrng.S index b7693fb06..25cc0ef0d 100644 --- a/libc/sysv/errfuns/elnrng.S +++ b/libc/sysv/errfuns/elnrng.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + elnrng: .leafprologue - .profilable - mov ELNRNG(%rip),%eax - mov %eax,errno(%rip) + mov ELNRNG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eloop.S b/libc/sysv/errfuns/eloop.S index 6fdc0c4fa..3de12f39f 100644 --- a/libc/sysv/errfuns/eloop.S +++ b/libc/sysv/errfuns/eloop.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eloop: .leafprologue - .profilable - mov ELOOP(%rip),%eax - mov %eax,errno(%rip) + mov ELOOP(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/emediumtype.S b/libc/sysv/errfuns/emediumtype.S index 40db57330..a3d7048ce 100644 --- a/libc/sysv/errfuns/emediumtype.S +++ b/libc/sysv/errfuns/emediumtype.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + emediumtype: .leafprologue - .profilable - mov EMEDIUMTYPE(%rip),%eax - mov %eax,errno(%rip) + mov EMEDIUMTYPE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/emfile.S b/libc/sysv/errfuns/emfile.S index eff8409c3..f89eb0153 100644 --- a/libc/sysv/errfuns/emfile.S +++ b/libc/sysv/errfuns/emfile.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + emfile: .leafprologue - .profilable - mov EMFILE(%rip),%eax - mov %eax,errno(%rip) + mov EMFILE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/emlink.S b/libc/sysv/errfuns/emlink.S index 5b3b21d93..2921a6f31 100644 --- a/libc/sysv/errfuns/emlink.S +++ b/libc/sysv/errfuns/emlink.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + emlink: .leafprologue - .profilable - mov EMLINK(%rip),%eax - mov %eax,errno(%rip) + mov EMLINK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/emsgsize.S b/libc/sysv/errfuns/emsgsize.S index 46231a27f..4553b3c89 100644 --- a/libc/sysv/errfuns/emsgsize.S +++ b/libc/sysv/errfuns/emsgsize.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + emsgsize: .leafprologue - .profilable - mov EMSGSIZE(%rip),%eax - mov %eax,errno(%rip) + mov EMSGSIZE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/emultihop.S b/libc/sysv/errfuns/emultihop.S index b91426f36..4b28d701b 100644 --- a/libc/sysv/errfuns/emultihop.S +++ b/libc/sysv/errfuns/emultihop.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + emultihop: .leafprologue - .profilable - mov EMULTIHOP(%rip),%eax - mov %eax,errno(%rip) + mov EMULTIHOP(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enametoolong.S b/libc/sysv/errfuns/enametoolong.S index a5829366d..2fc0821bb 100644 --- a/libc/sysv/errfuns/enametoolong.S +++ b/libc/sysv/errfuns/enametoolong.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enametoolong: .leafprologue - .profilable - mov ENAMETOOLONG(%rip),%eax - mov %eax,errno(%rip) + mov ENAMETOOLONG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enavail.S b/libc/sysv/errfuns/enavail.S index 521f690c5..130177053 100644 --- a/libc/sysv/errfuns/enavail.S +++ b/libc/sysv/errfuns/enavail.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enavail: .leafprologue - .profilable - mov ENAVAIL(%rip),%eax - mov %eax,errno(%rip) + mov ENAVAIL(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enetdown.S b/libc/sysv/errfuns/enetdown.S index 118aa6030..4876ef920 100644 --- a/libc/sysv/errfuns/enetdown.S +++ b/libc/sysv/errfuns/enetdown.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enetdown: .leafprologue - .profilable - mov ENETDOWN(%rip),%eax - mov %eax,errno(%rip) + mov ENETDOWN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enetreset.S b/libc/sysv/errfuns/enetreset.S index 88ec850fd..33044fed7 100644 --- a/libc/sysv/errfuns/enetreset.S +++ b/libc/sysv/errfuns/enetreset.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enetreset: .leafprologue - .profilable - mov ENETRESET(%rip),%eax - mov %eax,errno(%rip) + mov ENETRESET(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enetunreach.S b/libc/sysv/errfuns/enetunreach.S index a9d905e16..ea2037da1 100644 --- a/libc/sysv/errfuns/enetunreach.S +++ b/libc/sysv/errfuns/enetunreach.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enetunreach: .leafprologue - .profilable - mov ENETUNREACH(%rip),%eax - mov %eax,errno(%rip) + mov ENETUNREACH(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enfile.S b/libc/sysv/errfuns/enfile.S index eb2acb5f5..df349914e 100644 --- a/libc/sysv/errfuns/enfile.S +++ b/libc/sysv/errfuns/enfile.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enfile: .leafprologue - .profilable - mov ENFILE(%rip),%eax - mov %eax,errno(%rip) + mov ENFILE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enoano.S b/libc/sysv/errfuns/enoano.S index f67174be0..ec86713b3 100644 --- a/libc/sysv/errfuns/enoano.S +++ b/libc/sysv/errfuns/enoano.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enoano: .leafprologue - .profilable - mov ENOANO(%rip),%eax - mov %eax,errno(%rip) + mov ENOANO(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enobufs.S b/libc/sysv/errfuns/enobufs.S index cf9fbab58..fba6728f3 100644 --- a/libc/sysv/errfuns/enobufs.S +++ b/libc/sysv/errfuns/enobufs.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enobufs: .leafprologue - .profilable - mov ENOBUFS(%rip),%eax - mov %eax,errno(%rip) + mov ENOBUFS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enocsi.S b/libc/sysv/errfuns/enocsi.S index 62b180e9b..fdbd6614f 100644 --- a/libc/sysv/errfuns/enocsi.S +++ b/libc/sysv/errfuns/enocsi.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enocsi: .leafprologue - .profilable - mov ENOCSI(%rip),%eax - mov %eax,errno(%rip) + mov ENOCSI(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enodata.S b/libc/sysv/errfuns/enodata.S index ce83cd690..d41c30fad 100644 --- a/libc/sysv/errfuns/enodata.S +++ b/libc/sysv/errfuns/enodata.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enodata: .leafprologue - .profilable - mov ENODATA(%rip),%eax - mov %eax,errno(%rip) + mov ENODATA(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enodev.S b/libc/sysv/errfuns/enodev.S index 31cf7ea10..d2495a202 100644 --- a/libc/sysv/errfuns/enodev.S +++ b/libc/sysv/errfuns/enodev.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enodev: .leafprologue - .profilable - mov ENODEV(%rip),%eax - mov %eax,errno(%rip) + mov ENODEV(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enoent.S b/libc/sysv/errfuns/enoent.S index e454d29a5..5ffe65072 100644 --- a/libc/sysv/errfuns/enoent.S +++ b/libc/sysv/errfuns/enoent.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enoent: .leafprologue - .profilable - mov ENOENT(%rip),%eax - mov %eax,errno(%rip) + mov ENOENT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enoexec.S b/libc/sysv/errfuns/enoexec.S index f3ef662ba..582dc1061 100644 --- a/libc/sysv/errfuns/enoexec.S +++ b/libc/sysv/errfuns/enoexec.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enoexec: .leafprologue - .profilable - mov ENOEXEC(%rip),%eax - mov %eax,errno(%rip) + mov ENOEXEC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enokey.S b/libc/sysv/errfuns/enokey.S index 2dee4cd3c..69d99ddaa 100644 --- a/libc/sysv/errfuns/enokey.S +++ b/libc/sysv/errfuns/enokey.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enokey: .leafprologue - .profilable - mov ENOKEY(%rip),%eax - mov %eax,errno(%rip) + mov ENOKEY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enolck.S b/libc/sysv/errfuns/enolck.S index fc535dc1f..f4596bde2 100644 --- a/libc/sysv/errfuns/enolck.S +++ b/libc/sysv/errfuns/enolck.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enolck: .leafprologue - .profilable - mov ENOLCK(%rip),%eax - mov %eax,errno(%rip) + mov ENOLCK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enolink.S b/libc/sysv/errfuns/enolink.S index 30d71fca4..4d42660e7 100644 --- a/libc/sysv/errfuns/enolink.S +++ b/libc/sysv/errfuns/enolink.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enolink: .leafprologue - .profilable - mov ENOLINK(%rip),%eax - mov %eax,errno(%rip) + mov ENOLINK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enomedium.S b/libc/sysv/errfuns/enomedium.S index 6788bb69c..f83d3df1b 100644 --- a/libc/sysv/errfuns/enomedium.S +++ b/libc/sysv/errfuns/enomedium.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enomedium: .leafprologue - .profilable - mov ENOMEDIUM(%rip),%eax - mov %eax,errno(%rip) + mov ENOMEDIUM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enomem.S b/libc/sysv/errfuns/enomem.S index 6830592d7..cacd2fa07 100644 --- a/libc/sysv/errfuns/enomem.S +++ b/libc/sysv/errfuns/enomem.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enomem: .leafprologue - .profilable - mov ENOMEM(%rip),%eax - mov %eax,errno(%rip) + mov ENOMEM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enomsg.S b/libc/sysv/errfuns/enomsg.S index e20dbe123..a0efe0cea 100644 --- a/libc/sysv/errfuns/enomsg.S +++ b/libc/sysv/errfuns/enomsg.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enomsg: .leafprologue - .profilable - mov ENOMSG(%rip),%eax - mov %eax,errno(%rip) + mov ENOMSG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enonet.S b/libc/sysv/errfuns/enonet.S index 483b81c40..1a476bfb9 100644 --- a/libc/sysv/errfuns/enonet.S +++ b/libc/sysv/errfuns/enonet.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enonet: .leafprologue - .profilable - mov ENONET(%rip),%eax - mov %eax,errno(%rip) + mov ENONET(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enopkg.S b/libc/sysv/errfuns/enopkg.S index ab0a56c3e..f7eac5594 100644 --- a/libc/sysv/errfuns/enopkg.S +++ b/libc/sysv/errfuns/enopkg.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enopkg: .leafprologue - .profilable - mov ENOPKG(%rip),%eax - mov %eax,errno(%rip) + mov ENOPKG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enoprotoopt.S b/libc/sysv/errfuns/enoprotoopt.S index a10c0d1a3..4767e2858 100644 --- a/libc/sysv/errfuns/enoprotoopt.S +++ b/libc/sysv/errfuns/enoprotoopt.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enoprotoopt: .leafprologue - .profilable - mov ENOPROTOOPT(%rip),%eax - mov %eax,errno(%rip) + mov ENOPROTOOPT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enospc.S b/libc/sysv/errfuns/enospc.S index 226af4802..fdfd7b9bd 100644 --- a/libc/sysv/errfuns/enospc.S +++ b/libc/sysv/errfuns/enospc.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enospc: .leafprologue - .profilable - mov ENOSPC(%rip),%eax - mov %eax,errno(%rip) + mov ENOSPC(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enosr.S b/libc/sysv/errfuns/enosr.S index 6f81fa5db..a7c919c96 100644 --- a/libc/sysv/errfuns/enosr.S +++ b/libc/sysv/errfuns/enosr.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enosr: .leafprologue - .profilable - mov ENOSR(%rip),%eax - mov %eax,errno(%rip) + mov ENOSR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enostr.S b/libc/sysv/errfuns/enostr.S index 56d80b562..3a8814f4c 100644 --- a/libc/sysv/errfuns/enostr.S +++ b/libc/sysv/errfuns/enostr.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enostr: .leafprologue - .profilable - mov ENOSTR(%rip),%eax - mov %eax,errno(%rip) + mov ENOSTR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enosys.S b/libc/sysv/errfuns/enosys.S index 5b17c8dd0..09a1d2743 100644 --- a/libc/sysv/errfuns/enosys.S +++ b/libc/sysv/errfuns/enosys.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enosys: .leafprologue - .profilable - mov ENOSYS(%rip),%eax - mov %eax,errno(%rip) + mov ENOSYS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotblk.S b/libc/sysv/errfuns/enotblk.S index 142230a34..54eef8c9e 100644 --- a/libc/sysv/errfuns/enotblk.S +++ b/libc/sysv/errfuns/enotblk.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotblk: .leafprologue - .profilable - mov ENOTBLK(%rip),%eax - mov %eax,errno(%rip) + mov ENOTBLK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotconn.S b/libc/sysv/errfuns/enotconn.S index 9203834fa..7c7762630 100644 --- a/libc/sysv/errfuns/enotconn.S +++ b/libc/sysv/errfuns/enotconn.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotconn: .leafprologue - .profilable - mov ENOTCONN(%rip),%eax - mov %eax,errno(%rip) + mov ENOTCONN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotdir.S b/libc/sysv/errfuns/enotdir.S index 7f81664ae..9dd8d0e6f 100644 --- a/libc/sysv/errfuns/enotdir.S +++ b/libc/sysv/errfuns/enotdir.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotdir: .leafprologue - .profilable - mov ENOTDIR(%rip),%eax - mov %eax,errno(%rip) + mov ENOTDIR(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotempty.S b/libc/sysv/errfuns/enotempty.S index 2bbbbe454..a686905b0 100644 --- a/libc/sysv/errfuns/enotempty.S +++ b/libc/sysv/errfuns/enotempty.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotempty: .leafprologue - .profilable - mov ENOTEMPTY(%rip),%eax - mov %eax,errno(%rip) + mov ENOTEMPTY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotnam.S b/libc/sysv/errfuns/enotnam.S index 097f1520e..d96bb3c8e 100644 --- a/libc/sysv/errfuns/enotnam.S +++ b/libc/sysv/errfuns/enotnam.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotnam: .leafprologue - .profilable - mov ENOTNAM(%rip),%eax - mov %eax,errno(%rip) + mov ENOTNAM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotrecoverable.S b/libc/sysv/errfuns/enotrecoverable.S index d50bd8ac8..e94e3571d 100644 --- a/libc/sysv/errfuns/enotrecoverable.S +++ b/libc/sysv/errfuns/enotrecoverable.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotrecoverable: .leafprologue - .profilable - mov ENOTRECOVERABLE(%rip),%eax - mov %eax,errno(%rip) + mov ENOTRECOVERABLE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotsock.S b/libc/sysv/errfuns/enotsock.S index cf6ce3b16..622795711 100644 --- a/libc/sysv/errfuns/enotsock.S +++ b/libc/sysv/errfuns/enotsock.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotsock: .leafprologue - .profilable - mov ENOTSOCK(%rip),%eax - mov %eax,errno(%rip) + mov ENOTSOCK(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotsup.S b/libc/sysv/errfuns/enotsup.S index 4ea31b101..2018c9d13 100644 --- a/libc/sysv/errfuns/enotsup.S +++ b/libc/sysv/errfuns/enotsup.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotsup: .leafprologue - .profilable - mov ENOTSUP(%rip),%eax - mov %eax,errno(%rip) + mov ENOTSUP(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotty.S b/libc/sysv/errfuns/enotty.S index 5710e3eba..47bef3c9f 100644 --- a/libc/sysv/errfuns/enotty.S +++ b/libc/sysv/errfuns/enotty.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotty: .leafprologue - .profilable - mov ENOTTY(%rip),%eax - mov %eax,errno(%rip) + mov ENOTTY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enotuniq.S b/libc/sysv/errfuns/enotuniq.S index 212db238a..bf5d54242 100644 --- a/libc/sysv/errfuns/enotuniq.S +++ b/libc/sysv/errfuns/enotuniq.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enotuniq: .leafprologue - .profilable - mov ENOTUNIQ(%rip),%eax - mov %eax,errno(%rip) + mov ENOTUNIQ(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/enxio.S b/libc/sysv/errfuns/enxio.S index b6a1cd05c..03cb4d809 100644 --- a/libc/sysv/errfuns/enxio.S +++ b/libc/sysv/errfuns/enxio.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + enxio: .leafprologue - .profilable - mov ENXIO(%rip),%eax - mov %eax,errno(%rip) + mov ENXIO(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eopnotsupp.S b/libc/sysv/errfuns/eopnotsupp.S index 1cb69fbda..07cf5dbd2 100644 --- a/libc/sysv/errfuns/eopnotsupp.S +++ b/libc/sysv/errfuns/eopnotsupp.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eopnotsupp: .leafprologue - .profilable - mov EOPNOTSUPP(%rip),%eax - mov %eax,errno(%rip) + mov EOPNOTSUPP(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eoverflow.S b/libc/sysv/errfuns/eoverflow.S index 4f92fbd51..6408fe872 100644 --- a/libc/sysv/errfuns/eoverflow.S +++ b/libc/sysv/errfuns/eoverflow.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eoverflow: .leafprologue - .profilable - mov EOVERFLOW(%rip),%eax - mov %eax,errno(%rip) + mov EOVERFLOW(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eownerdead.S b/libc/sysv/errfuns/eownerdead.S index bd8841f19..cf7a3c63e 100644 --- a/libc/sysv/errfuns/eownerdead.S +++ b/libc/sysv/errfuns/eownerdead.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eownerdead: .leafprologue - .profilable - mov EOWNERDEAD(%rip),%eax - mov %eax,errno(%rip) + mov EOWNERDEAD(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eperm.S b/libc/sysv/errfuns/eperm.S index dd01b6f20..aded81083 100644 --- a/libc/sysv/errfuns/eperm.S +++ b/libc/sysv/errfuns/eperm.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eperm: .leafprologue - .profilable - mov EPERM(%rip),%eax - mov %eax,errno(%rip) + mov EPERM(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/epfnosupport.S b/libc/sysv/errfuns/epfnosupport.S index c3c8b53c4..105fbee68 100644 --- a/libc/sysv/errfuns/epfnosupport.S +++ b/libc/sysv/errfuns/epfnosupport.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + epfnosupport: .leafprologue - .profilable - mov EPFNOSUPPORT(%rip),%eax - mov %eax,errno(%rip) + mov EPFNOSUPPORT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/epipe.S b/libc/sysv/errfuns/epipe.S index 605075589..1fff95c8d 100644 --- a/libc/sysv/errfuns/epipe.S +++ b/libc/sysv/errfuns/epipe.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + epipe: .leafprologue - .profilable - mov EPIPE(%rip),%eax - mov %eax,errno(%rip) + mov EPIPE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eproto.S b/libc/sysv/errfuns/eproto.S index e92a3d627..4965378ae 100644 --- a/libc/sysv/errfuns/eproto.S +++ b/libc/sysv/errfuns/eproto.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eproto: .leafprologue - .profilable - mov EPROTO(%rip),%eax - mov %eax,errno(%rip) + mov EPROTO(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eprotonosupport.S b/libc/sysv/errfuns/eprotonosupport.S index 72eaf04ac..3b3fc92ec 100644 --- a/libc/sysv/errfuns/eprotonosupport.S +++ b/libc/sysv/errfuns/eprotonosupport.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eprotonosupport: .leafprologue - .profilable - mov EPROTONOSUPPORT(%rip),%eax - mov %eax,errno(%rip) + mov EPROTONOSUPPORT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eprototype.S b/libc/sysv/errfuns/eprototype.S index a5991f155..ad1471e01 100644 --- a/libc/sysv/errfuns/eprototype.S +++ b/libc/sysv/errfuns/eprototype.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eprototype: .leafprologue - .profilable - mov EPROTOTYPE(%rip),%eax - mov %eax,errno(%rip) + mov EPROTOTYPE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/erange.S b/libc/sysv/errfuns/erange.S index 2f4100027..d541322c9 100644 --- a/libc/sysv/errfuns/erange.S +++ b/libc/sysv/errfuns/erange.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + erange: .leafprologue - .profilable - mov ERANGE(%rip),%eax - mov %eax,errno(%rip) + mov ERANGE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eremchg.S b/libc/sysv/errfuns/eremchg.S index 3a32d3122..4366c696a 100644 --- a/libc/sysv/errfuns/eremchg.S +++ b/libc/sysv/errfuns/eremchg.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eremchg: .leafprologue - .profilable - mov EREMCHG(%rip),%eax - mov %eax,errno(%rip) + mov EREMCHG(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eremote.S b/libc/sysv/errfuns/eremote.S index e59f60d58..40b3595e0 100644 --- a/libc/sysv/errfuns/eremote.S +++ b/libc/sysv/errfuns/eremote.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eremote: .leafprologue - .profilable - mov EREMOTE(%rip),%eax - mov %eax,errno(%rip) + mov EREMOTE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eremoteio.S b/libc/sysv/errfuns/eremoteio.S index de0bae9ab..5631cfb58 100644 --- a/libc/sysv/errfuns/eremoteio.S +++ b/libc/sysv/errfuns/eremoteio.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eremoteio: .leafprologue - .profilable - mov EREMOTEIO(%rip),%eax - mov %eax,errno(%rip) + mov EREMOTEIO(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/erestart.S b/libc/sysv/errfuns/erestart.S index 86332af1f..668533c72 100644 --- a/libc/sysv/errfuns/erestart.S +++ b/libc/sysv/errfuns/erestart.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + erestart: .leafprologue - .profilable - mov ERESTART(%rip),%eax - mov %eax,errno(%rip) + mov ERESTART(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/erfkill.S b/libc/sysv/errfuns/erfkill.S index bed98ca59..4f509f686 100644 --- a/libc/sysv/errfuns/erfkill.S +++ b/libc/sysv/errfuns/erfkill.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + erfkill: .leafprologue - .profilable - mov ERFKILL(%rip),%eax - mov %eax,errno(%rip) + mov ERFKILL(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/erofs.S b/libc/sysv/errfuns/erofs.S index ddedebc92..0468b5c38 100644 --- a/libc/sysv/errfuns/erofs.S +++ b/libc/sysv/errfuns/erofs.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + erofs: .leafprologue - .profilable - mov EROFS(%rip),%eax - mov %eax,errno(%rip) + mov EROFS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eshutdown.S b/libc/sysv/errfuns/eshutdown.S index 2cbe5943f..833f3f2c6 100644 --- a/libc/sysv/errfuns/eshutdown.S +++ b/libc/sysv/errfuns/eshutdown.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eshutdown: .leafprologue - .profilable - mov ESHUTDOWN(%rip),%eax - mov %eax,errno(%rip) + mov ESHUTDOWN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/esocktnosupport.S b/libc/sysv/errfuns/esocktnosupport.S index 03d7e2c8d..d6d78a6c4 100644 --- a/libc/sysv/errfuns/esocktnosupport.S +++ b/libc/sysv/errfuns/esocktnosupport.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + esocktnosupport: .leafprologue - .profilable - mov ESOCKTNOSUPPORT(%rip),%eax - mov %eax,errno(%rip) + mov ESOCKTNOSUPPORT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/espipe.S b/libc/sysv/errfuns/espipe.S index 79fe0c90c..e23d80fca 100644 --- a/libc/sysv/errfuns/espipe.S +++ b/libc/sysv/errfuns/espipe.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + espipe: .leafprologue - .profilable - mov ESPIPE(%rip),%eax - mov %eax,errno(%rip) + mov ESPIPE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/esrch.S b/libc/sysv/errfuns/esrch.S index d4dd14a4f..a49b81a8a 100644 --- a/libc/sysv/errfuns/esrch.S +++ b/libc/sysv/errfuns/esrch.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + esrch: .leafprologue - .profilable - mov ESRCH(%rip),%eax - mov %eax,errno(%rip) + mov ESRCH(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/esrmnt.S b/libc/sysv/errfuns/esrmnt.S index 794a4e7f7..fb19cda90 100644 --- a/libc/sysv/errfuns/esrmnt.S +++ b/libc/sysv/errfuns/esrmnt.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + esrmnt: .leafprologue - .profilable - mov ESRMNT(%rip),%eax - mov %eax,errno(%rip) + mov ESRMNT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/estale.S b/libc/sysv/errfuns/estale.S index b89c58594..40e0e847a 100644 --- a/libc/sysv/errfuns/estale.S +++ b/libc/sysv/errfuns/estale.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + estale: .leafprologue - .profilable - mov ESTALE(%rip),%eax - mov %eax,errno(%rip) + mov ESTALE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/estrpipe.S b/libc/sysv/errfuns/estrpipe.S index 15156b25f..8051599dc 100644 --- a/libc/sysv/errfuns/estrpipe.S +++ b/libc/sysv/errfuns/estrpipe.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + estrpipe: .leafprologue - .profilable - mov ESTRPIPE(%rip),%eax - mov %eax,errno(%rip) + mov ESTRPIPE(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/etime.S b/libc/sysv/errfuns/etime.S index 4ab7872b4..551d86c09 100644 --- a/libc/sysv/errfuns/etime.S +++ b/libc/sysv/errfuns/etime.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + etime: .leafprologue - .profilable - mov ETIME(%rip),%eax - mov %eax,errno(%rip) + mov ETIME(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/etimedout.S b/libc/sysv/errfuns/etimedout.S index 10260ecaf..eff3c3292 100644 --- a/libc/sysv/errfuns/etimedout.S +++ b/libc/sysv/errfuns/etimedout.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + etimedout: .leafprologue - .profilable - mov ETIMEDOUT(%rip),%eax - mov %eax,errno(%rip) + mov ETIMEDOUT(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/etoomanyrefs.S b/libc/sysv/errfuns/etoomanyrefs.S index 28fd32b54..2fa0752a3 100644 --- a/libc/sysv/errfuns/etoomanyrefs.S +++ b/libc/sysv/errfuns/etoomanyrefs.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + etoomanyrefs: .leafprologue - .profilable - mov ETOOMANYREFS(%rip),%eax - mov %eax,errno(%rip) + mov ETOOMANYREFS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/etxtbsy.S b/libc/sysv/errfuns/etxtbsy.S index 75ce6c84b..d3e390030 100644 --- a/libc/sysv/errfuns/etxtbsy.S +++ b/libc/sysv/errfuns/etxtbsy.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + etxtbsy: .leafprologue - .profilable - mov ETXTBSY(%rip),%eax - mov %eax,errno(%rip) + mov ETXTBSY(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/euclean.S b/libc/sysv/errfuns/euclean.S index 1f960a7e9..af9217e97 100644 --- a/libc/sysv/errfuns/euclean.S +++ b/libc/sysv/errfuns/euclean.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + euclean: .leafprologue - .profilable - mov EUCLEAN(%rip),%eax - mov %eax,errno(%rip) + mov EUCLEAN(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eunatch.S b/libc/sysv/errfuns/eunatch.S index a88f75f72..97b639674 100644 --- a/libc/sysv/errfuns/eunatch.S +++ b/libc/sysv/errfuns/eunatch.S @@ -1,11 +1,13 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eunatch: .leafprologue - .profilable - mov EUNATCH(%rip),%eax - mov %eax,errno(%rip) + mov EUNATCH(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/eusers.S b/libc/sysv/errfuns/eusers.S index 57c71de1a..41a1e5a7d 100644 --- a/libc/sysv/errfuns/eusers.S +++ b/libc/sysv/errfuns/eusers.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + eusers: .leafprologue - .profilable - mov EUSERS(%rip),%eax - mov %eax,errno(%rip) + mov EUSERS(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/exdev.S b/libc/sysv/errfuns/exdev.S index b0a43ec5c..2f6b33ba5 100644 --- a/libc/sysv/errfuns/exdev.S +++ b/libc/sysv/errfuns/exdev.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + exdev: .leafprologue - .profilable - mov EXDEV(%rip),%eax - mov %eax,errno(%rip) + mov EXDEV(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/sysv/errfuns/exfull.S b/libc/sysv/errfuns/exfull.S index aa552dec8..22d7f6a93 100644 --- a/libc/sysv/errfuns/exfull.S +++ b/libc/sysv/errfuns/exfull.S @@ -1,10 +1,12 @@ #include "libc/macros.internal.h" .text.unlikely +.section .privileged,"ax",@progbits + exfull: .leafprologue - .profilable - mov EXFULL(%rip),%eax - mov %eax,errno(%rip) + mov EXFULL(%rip),%ecx + .errno + mov %ecx,(%rax) push $-1 pop %rax .leafepilogue diff --git a/libc/nexgen32e/errno.c b/libc/sysv/errno.greg.c similarity index 76% rename from libc/nexgen32e/errno.c rename to libc/sysv/errno.greg.c index e0ac0743b..c0476db59 100644 --- a/libc/nexgen32e/errno.c +++ b/libc/sysv/errno.greg.c @@ -16,7 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" +#include "libc/bits/weaken.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/intrin/threaded.internal.h" /** * Global variable for last error. @@ -30,13 +33,23 @@ * @see libc/sysv/errfuns.h * @see __errno_location() stable abi */ -int errno; +errno_t __errno; +int __errno_index; -/** - * Returns address of errno. - * - * @note this is needed by gdb - */ -int *__errno_location(void) { - return &errno; +privileged nocallersavedregisters errno_t *(__errno_location)(void) { + char *tib; + if (!__hastls) { + return &__errno; + } else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { + asm("mov\t%%fs:0,%0" : "=a"(tib)); + return (errno_t *)(tib + 0x3c); + } else if (IsXnu()) { + asm("mov\t%%gs:0x30,%0" : "=a"(tib)); + return (errno_t *)(tib + 0x3c); + } else if (IsWindows()) { + asm("mov\t%%gs:0x30,%0" : "=a"(tib)); + return (errno_t *)(tib + 0x1480 + __errno_index * 8); + } else { + return &__errno; + } } diff --git a/libc/sysv/gen.sh b/libc/sysv/gen.sh index b450111cd..b70ffdfd9 100644 --- a/libc/sysv/gen.sh +++ b/libc/sysv/gen.sh @@ -47,14 +47,15 @@ errfun() { ERRNO="$2" { printf '#include "libc/macros.internal.h"\n.text.unlikely\n\n' + printf '.section .privileged,"ax",@progbits\n\n' printf '%s:' "$NAME" if [ "${#NAME}" -gt 6 ]; then printf '\n' fi printf ' .leafprologue - .profilable - mov %s(%%rip),%%eax - mov %%eax,errno(%%rip) + mov %s(%%rip),%%ecx + .errno + mov %%ecx,(%%rax) push $-1 pop %%rax .leafepilogue diff --git a/libc/sysv/macros.internal.h b/libc/sysv/macros.internal.h index 88d1057c0..b6e564734 100644 --- a/libc/sysv/macros.internal.h +++ b/libc/sysv/macros.internal.h @@ -16,6 +16,7 @@ */ .macro .scall name:req num:req kw1 kw2 + .section .privileged,"ax",@progbits .ifnb \kw2 .align 16 \name: movabs $\num,%rax diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 81147c104..b32464dc3 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -126,7 +126,9 @@ systemfive_error: .endfn systemfive_error,globl,hidden #endif systemfive_errno: - mov %eax,errno(%rip) # normalize to c library convention + xchg %eax,%ecx + .errno + mov %ecx,(%rax) # normalize to c library convention push $-1 # negative one is only error result pop %rax # the push pop is to save code size ret diff --git a/libc/sysv/sysv.mk b/libc/sysv/sysv.mk index 42d389fc7..5811daee5 100644 --- a/libc/sysv/sysv.mk +++ b/libc/sysv/sysv.mk @@ -37,6 +37,7 @@ LIBC_SYSV_A_FILES := \ libc/sysv/restorert.S \ libc/sysv/syscall.S \ libc/sysv/systemfive.S \ + libc/sysv/errno.greg.c \ libc/sysv/strace.greg.c \ libc/sysv/describeos.greg.c \ $(wildcard libc/sysv/consts/*) \ @@ -74,6 +75,10 @@ o/libc/sysv/consts/syscon.internal.inc: \ libc/macros-cpp.internal.inc \ libc/macros.internal.inc +o/$(MODE)/libc/sysv/errno.greg.o: \ + OVERRIDE_CFLAGS += \ + $(NO_MAGIC) + #─────────────────────────────────────────────────────────────────────────────── LIBC_SYSV_CALLS = \ diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 50c68da0f..dcede6842 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -5,6 +5,8 @@ #include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/intrin/spinlock.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/time/time.h" @@ -179,17 +181,8 @@ static struct tm *localtime_timesub(time_t const *, int_fast32_t, static bool localtime_typesequiv(struct state const *, int, int); static bool localtime_tzparse(char const *, struct state *, struct state *); -#ifdef ALL_STATE static struct state * lclptr; static struct state * gmtptr; -#endif /* defined ALL_STATE */ - -#ifndef ALL_STATE -static struct state lclmem; -static struct state gmtmem; -#define lclptr (&lclmem) -#define gmtptr (&gmtmem) -#endif /* State Farm */ #ifndef TZ_STRLEN_MAX #define TZ_STRLEN_MAX 255 @@ -736,7 +729,6 @@ localtime_tzloadbody(char const *name, struct state *sp, bool doextend, static int localtime_tzload(char const *name, struct state *sp, bool doextend) { -#ifdef ALL_STATE union local_storage *lsp = malloc(sizeof *lsp); if (!lsp) { return HAVE_MALLOC_ERRNO ? errno : ENOMEM; @@ -745,17 +737,6 @@ localtime_tzload(char const *name, struct state *sp, bool doextend) free(lsp); return err; } -#else - int i; - volatile char *p; - volatile unsigned x; - union local_storage ls; /* 70+ kilobytes */ - p = (char *)&ls; - for (x = i = 0; i < sizeof(ls); i += 4096) { - x += p[i]; /* make sure tzdata doesn't smash the stack */ - } - return localtime_tzloadbody(name, sp, doextend, &ls); -#endif } static bool @@ -1393,6 +1374,11 @@ zoneinit(struct state *sp, char const *name) } } +static void +FreeLocaltime(void *p) { + free(p); +} + static void localtime_tzset_unlocked(void) { @@ -1403,10 +1389,10 @@ localtime_tzset_unlocked(void) ? lcl_is_set < 0 : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0) return; -#ifdef ALL_STATE - if (! sp) + if (!sp) { lclptr = sp = malloc(sizeof *lclptr); -#endif /* defined ALL_STATE */ + __cxa_atexit(FreeLocaltime, sp, 0); + } if (sp) { if (zoneinit(sp, name) != 0) zoneinit(sp, ""); @@ -1426,6 +1412,11 @@ tzset(void) unlock(); } +static void +FreeGmt(void *p) { + free(p); +} + static void localtime_gmtcheck(void) { @@ -1433,9 +1424,8 @@ localtime_gmtcheck(void) if (lock() != 0) return; if (! gmt_is_set) { -#ifdef ALL_STATE gmtptr = malloc(sizeof *gmtptr); -#endif + __cxa_atexit(FreeGmt, gmtptr, 0); if (gmtptr) gmtload(gmtptr); gmt_is_set = true; diff --git a/libc/time/strftime.c b/libc/time/strftime.c index 8ab0ae2d1..ab673610a 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -569,6 +569,7 @@ label: * strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123 * * @return bytes copied excluding nul, or 0 on error + * @see FormatHttpDateTime() */ size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *t) diff --git a/libc/time/time.mk b/libc/time/time.mk index da673d944..d3eed3777 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -53,10 +53,6 @@ $(LIBC_TIME_A).pkg: \ $(LIBC_TIME_A_OBJS) \ $(foreach x,$(LIBC_TIME_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/time/localtime.o: \ - OVERRIDE_CFLAGS += \ - $(OLD_CODE) - o/$(MODE)/libc/time/strftime.o: \ OVERRIDE_CFLAGS += \ -fno-jump-tables @@ -66,10 +62,6 @@ o/$(MODE)/libc/time/localtime.o: \ -fdata-sections \ -ffunction-sections -o/$(MODE)/libc/time/localtime.o: \ - OVERRIDE_CPPFLAGS += \ - -DSTACK_FRAME_UNLIMITED - o/$(MODE)/libc/time/now.o: \ OVERRIDE_CFLAGS += \ -O3 diff --git a/net/http/formathttpdatetime.c b/net/http/formathttpdatetime.c index 9a5258fd5..bc9ebc808 100644 --- a/net/http/formathttpdatetime.c +++ b/net/http/formathttpdatetime.c @@ -27,6 +27,15 @@ * * Sun, 04 Oct 2020 19:50:10 GMT * + * This function is the same as: + * + * strftime(p, 30, "%a, %d %b %Y %H:%M:%S %Z", tm) + * + * Except this function goes 10x faster: + * + * FormatHttpDateTime l: 25𝑐 8𝑛𝑠 + * strftime l: 709𝑐 229𝑛𝑠 + * * @param tm must be zulu see gmtime_r() and nowl() * @see ParseHttpDateTime() */ diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index 6ce94214b..1db449e66 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -19,7 +19,9 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" +#include "libc/mem/mem.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" @@ -28,7 +30,7 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -char *stack; +char *stack, *tls; int x, me, thechilde; _Alignas(64) volatile char lock; @@ -37,18 +39,31 @@ void SetUp(void) { lock = 0; me = gettid(); thechilde = 0; + tls = calloc(1, 512); + *(intptr_t *)tls = (intptr_t)tls; + *(intptr_t *)(tls + 0x30) = (intptr_t)tls; + *(int *)(tls + 0x3c) = 31337; ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0))); } void TearDown(void) { - tkill(thechilde, SIGKILL), errno = 0; + if (thechilde) { + tkill(thechilde, SIGKILL); + errno = 0; + } sched_yield(); - EXPECT_SYS(0, 0, munmap(stack, GetStackSize())); + free(tls); } int CloneTest1(void *arg) { x = 42; + if (!IsWindows()) { + ASSERT_EQ(31337, errno); + } else { + errno = 31337; + ASSERT_EQ(31337, errno); + } ASSERT_EQ(23, (intptr_t)arg); thechilde = gettid(); ASSERT_NE(gettid(), getpid()); @@ -56,15 +71,33 @@ int CloneTest1(void *arg) { return 0; } +int DoNothing(void *arg) { + return 0; +} + +void __setup_tls(void); + TEST(clone, test1) { int tid; _spinlock(&lock); ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND, - (void *)23, 0, 0, 0, 0))); + CLONE_SIGHAND | CLONE_SETTLS, + (void *)23, 0, tls, 512, 0))); _spinlock(&lock); ASSERT_EQ(42, x); ASSERT_NE(me, tid); ASSERT_EQ(tid, thechilde); + ASSERT_EQ(0, errno); + errno = 31337; + ASSERT_EQ(31337, errno); + + return; + intptr_t *p; + asm("movq\t%%fs:0x30,%0" : "=a"(p)); + kprintf("%fs:0x30 = %p\n", p); + for (int i = 0; i < 64; ++i) { + kprintf("0x%.5x = %p\n", i * 8, p[i]); + } + kprintf("\n"); } diff --git a/test/net/http/formathttpdatetime_test.c b/test/net/http/formathttpdatetime_test.c new file mode 100644 index 000000000..71b4b441d --- /dev/null +++ b/test/net/http/formathttpdatetime_test.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 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/testlib/ezbench.h" +#include "libc/testlib/testlib.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" +#include "net/http/http.h" + +TEST(FormatHttpDateTime, test) { + char p[30]; + struct tm *tm; + int64_t t = 0x62820bcd; + tm = gmtime(&t); + FormatHttpDateTime(p, tm); + ASSERT_STREQ("Mon, 16 May 2022 08:31:09 GMT", p); +} + +TEST(FormatHttpDateTime, testStrftime) { + char p[30]; + struct tm *tm; + int64_t t = 0x62820bcd; + tm = gmtime(&t); + strftime(p, sizeof(p), "%a, %d %b %Y %H:%M:%S %Z", tm); + ASSERT_STREQ("Mon, 16 May 2022 08:31:09 GMT", p); +} + +BENCH(FormatHttpDateTime, bench) { + char p[30]; + struct tm *tm; + int64_t t = 0x62820bcd; + tm = gmtime(&t); + EZBENCH2("FormatHttpDateTime", donothing, FormatHttpDateTime(p, tm)); + EZBENCH2("strftime", donothing, + strftime(p, sizeof(p), "%a, %d %b %Y %H:%M:%S %Z", tm)); +} diff --git a/third_party/libcxx/libcxx.mk b/third_party/libcxx/libcxx.mk index df2414a9f..500a96445 100644 --- a/third_party/libcxx/libcxx.mk +++ b/third_party/libcxx/libcxx.mk @@ -69,7 +69,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \ THIRD_PARTY_LIBCXX_A_SRCS = \ $(THIRD_PARTY_LIBCXX_A_SRCS_S) \ - $(THIRD_PARTY_LIBCXX_A_SRCS_C) + $(THIRD_PARTY_LIBCXX_A_SRCS_CC) THIRD_PARTY_LIBCXX_A_OBJS = \ $(THIRD_PARTY_LIBCXX_A_SRCS_S:%.S=o/$(MODE)/%.o) \ diff --git a/third_party/libcxx/random.cc b/third_party/libcxx/random.cc index cb75edb4b..a09a5c9e1 100644 --- a/third_party/libcxx/random.cc +++ b/third_party/libcxx/random.cc @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "libc/rand/rand.h" #include "third_party/libcxx/__config" #if defined(_LIBCPP_USING_WIN32_RANDOM) @@ -24,15 +25,6 @@ #include "third_party/libcxx/stdio.h" #include "third_party/libcxx/stdlib.h" -#if defined(_LIBCPP_USING_GETENTROPY) -#include "libc/isystem/sys/random.h" -#elif defined(_LIBCPP_USING_DEV_RANDOM) -#include "third_party/libcxx/fcntl.h" -#include "third_party/libcxx/unistd.h" -#elif defined(_LIBCPP_USING_NACL_RANDOM) -#include "third_party/libcxx/nacl/nacl_random.h" -#endif - _LIBCPP_BEGIN_NAMESPACE_STD #if defined(_LIBCPP_USING_GETENTROPY) diff --git a/third_party/libcxx/stdexcept_default.hh b/third_party/libcxx/stdexcept_default.hh index 40e6ab78e..63d489286 100644 --- a/third_party/libcxx/stdexcept_default.hh +++ b/third_party/libcxx/stdexcept_default.hh @@ -9,12 +9,6 @@ #include "third_party/libcxx/refstring.hh" #include "third_party/libcxx/string" -/* For _LIBCPPABI_VERSION */ -#if !defined(_LIBCPP_BUILDING_HAS_NO_ABI_LIBRARY) && \ - (defined(LIBCXX_BUILDING_LIBCXXABI) || defined(LIBCXXRT)) -#include "third_party/libcxx/cxxabi.h" -#endif - static_assert(sizeof(std::__libcpp_refstring) == sizeof(const char*), ""); namespace std // purposefully not using versioning namespace diff --git a/third_party/libcxx/system_error.cc b/third_party/libcxx/system_error.cc index 3e1185d6b..4f00c9650 100644 --- a/third_party/libcxx/system_error.cc +++ b/third_party/libcxx/system_error.cc @@ -19,10 +19,6 @@ #include "third_party/libcxx/string.h" #include "third_party/libcxx/__debug" -#if defined(__ANDROID__) -#include "third_party/libcxx/android/api-level.h" -#endif - _LIBCPP_BEGIN_NAMESPACE_STD // class error_category diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index e9915859f..2005072d1 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -81,6 +81,7 @@ o/$(MODE)/third_party/lua/lua.com: \ @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/lua/.lua/.symtab +o/$(MODE)/third_party/lua/lmathlib.o \ o//third_party/lua/lgc.o: \ OVERRIDE_CFLAGS += \ -O2 diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 21ff5d215..7d193ca23 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -1152,6 +1152,7 @@ THIRD_PARTY_PYTHON_STAGE2_A_DIRECTDEPS = \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_SQLITE3 \ THIRD_PARTY_ZLIB \ + THIRD_PARTY_XED \ TOOL_ARGS THIRD_PARTY_PYTHON_STAGE2_A_DEPS = \ @@ -2090,6 +2091,7 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DIRECTDEPS = \ THIRD_PARTY_PYTHON_STAGE2 \ THIRD_PARTY_PYTHON_PYTEST \ THIRD_PARTY_LINENOISE \ + THIRD_PARTY_XED \ TOOL_ARGS THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DEPS = \ @@ -4252,6 +4254,7 @@ THIRD_PARTY_PYTHON_PYTHON_DIRECTDEPS = \ THIRD_PARTY_PYTHON_STAGE1 \ THIRD_PARTY_PYTHON_STAGE2 \ THIRD_PARTY_PYTHON_PYTEST \ + THIRD_PARTY_XED \ TOOL_ARGS o/$(MODE)/third_party/python/python.pkg: \ @@ -4298,6 +4301,7 @@ THIRD_PARTY_PYTHON_FREEZE_DIRECTDEPS = \ LIBC_UNICODE \ LIBC_X \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_XED \ THIRD_PARTY_PYTHON_STAGE1 o/$(MODE)/third_party/python/freeze.pkg: \ diff --git a/third_party/python/repl.c b/third_party/python/repl.c index cbb5d7f76..d488fc408 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -9,10 +9,17 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" +#include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" @@ -20,7 +27,9 @@ #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sig.h" +#include "libc/time/time.h" #include "libc/unicode/locale.h" #include "libc/x/x.h" #include "third_party/linenoise/linenoise.h" @@ -41,6 +50,7 @@ #include "third_party/python/Include/pythonrun.h" #include "third_party/python/Include/unicodeobject.h" #include "third_party/python/Include/yoink.h" +#include "third_party/xed/x86.h" #include "tool/args/args.h" /* clang-format off */ diff --git a/third_party/xed/xed.mk b/third_party/xed/xed.mk index 0a45e57c8..26791595e 100644 --- a/third_party/xed/xed.mk +++ b/third_party/xed/xed.mk @@ -40,7 +40,7 @@ THIRD_PARTY_XED_A_DIRECTDEPS = \ THIRD_PARTY_XED_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_XED_A_DIRECTDEPS),$($(x)))) -o/$(MODE)/third_party/xed/x86ild.greg.o: \ +o/$(MODE)/third_party/xed/x86ild.greg.o: \ OVERRIDE_CFLAGS += \ -mstringop-strategy=unrolled_loop @@ -53,6 +53,10 @@ $(THIRD_PARTY_XED_A).pkg: \ $(THIRD_PARTY_XED_A_OBJS) \ $(foreach x,$(THIRD_PARTY_XED_A_DIRECTDEPS),$($(x)_A).pkg) +o/$(MODE)/third_party/xed/x86ild.greg.o: \ + OVERRIDE_CFLAGS += \ + -O3 + 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/zlib/gz/gzread.c b/third_party/zlib/gz/gzread.c index 73558600f..c514a81af 100644 --- a/third_party/zlib/gz/gzread.c +++ b/third_party/zlib/gz/gzread.c @@ -1,5 +1,9 @@ #include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/limits.h" +#include "libc/str/str.h" #include "third_party/zlib/gz/gzguts.inc" +#include "third_party/zlib/zlib.h" // clang-format off /* gzread.c -- zlib functions for reading gzip files diff --git a/tool/args/args.c b/tool/args/args.c index 204341f16..4bed2a002 100644 --- a/tool/args/args.c +++ b/tool/args/args.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -107,8 +108,16 @@ int LoadZipArgsImpl(int *argc, char ***argv, char *data) { * If the special argument `...` *is* encountered, then it'll be * replaced with whatever CLI args were specified by the user. * - * @return 0 on success, or -1 w/ errno + * @return 0 on success, or -1 if not found w/o errno clobber */ int LoadZipArgs(int *argc, char ***argv) { - return LoadZipArgsImpl(argc, argv, xslurp("/zip/.args", 0)); + int e; + char *p; + e = errno; + if ((p = xslurp("/zip/.args", 0))) { + return LoadZipArgsImpl(argc, argv, p); + } else { + errno = e; + return -1; + } } diff --git a/tool/args/args.mk b/tool/args/args.mk index 0cd6dba4f..03618a287 100644 --- a/tool/args/args.mk +++ b/tool/args/args.mk @@ -21,6 +21,7 @@ TOOL_ARGS_A_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_STR \ LIBC_STUBS \ + LIBC_SYSV \ LIBC_X \ LIBC_ZIPOS diff --git a/tool/build/lib/errnos.S b/tool/build/lib/errnos.S index d665d880e..7f09f1759 100644 --- a/tool/build/lib/errnos.S +++ b/tool/build/lib/errnos.S @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" - .macro .errno local:req linux:req + .macro .e local:req linux:req .globl \local .long \local-kLinuxErrnos .byte \linux @@ -30,94 +30,94 @@ .rodata .align 4 kLinuxErrnos: - .errno EPERM,1 - .errno ENOENT,2 - .errno ESRCH,3 - .errno EINTR,4 - .errno EIO,5 - .errno ENXIO,6 - .errno E2BIG,7 - .errno ENOEXEC,8 - .errno EBADF,9 - .errno ECHILD,10 - .errno EAGAIN,11 - .errno ENOMEM,12 - .errno EACCES,13 - .errno EFAULT,14 - .errno ENOTBLK,15 - .errno EBUSY,16 - .errno EEXIST,17 - .errno EXDEV,18 - .errno ENODEV,19 - .errno ENOTDIR,20 - .errno EISDIR,21 - .errno EINVAL,22 - .errno ENFILE,23 - .errno EMFILE,24 - .errno ENOTTY,25 - .errno ETXTBSY,26 - .errno EFBIG,27 - .errno ENOSPC,28 - .errno ESPIPE,29 - .errno EROFS,30 - .errno EMLINK,31 - .errno EPIPE,32 - .errno EDOM,33 - .errno ERANGE,34 - .errno EDEADLK,35 - .errno ENAMETOOLONG,36 - .errno ENOLCK,37 - .errno ENOSYS,38 - .errno ENOTEMPTY,39 - .errno ELOOP,40 - .errno ENOMSG,42 - .errno EIDRM,43 - .errno EUSERS,87 - .errno ENOTSOCK,88 - .errno EDESTADDRREQ,89 - .errno EMSGSIZE,90 - .errno EPROTOTYPE,91 - .errno ENOPROTOOPT,92 - .errno EPROTONOSUPPORT,93 - .errno ESOCKTNOSUPPORT,94 - .errno EOPNOTSUPP,95 - .errno EPFNOSUPPORT,96 - .errno EAFNOSUPPORT,97 - .errno EADDRINUSE,98 - .errno EADDRNOTAVAIL,99 - .errno EL2NSYNC,45 - .errno EL3HLT,46 - .errno EL3RST,47 - .errno ELNRNG,48 - .errno ETIME,62 - .errno ENONET,64 - .errno EREMOTE,66 - .errno EPROTO,71 - .errno EBADMSG,74 - .errno EOVERFLOW,75 - .errno EILSEQ,84 - .errno ERESTART,85 - .errno ENETDOWN,100 - .errno ENETUNREACH,101 - .errno ENETRESET,102 - .errno ECONNABORTED,103 - .errno ECONNRESET,104 - .errno ENOBUFS,105 - .errno EISCONN,106 - .errno ENOTCONN,107 - .errno ESHUTDOWN,108 - .errno ETOOMANYREFS,109 - .errno ETIMEDOUT,110 - .errno ECONNREFUSED,111 - .errno EHOSTDOWN,112 - .errno EHOSTUNREACH,113 - .errno EALREADY,114 - .errno EINPROGRESS,115 - .errno ESTALE,116 - .errno EDQUOT,122 - .errno ECANCELED,125 - .errno EOWNERDEAD,130 - .errno ENOTRECOVERABLE,131 + .e EPERM,1 + .e ENOENT,2 + .e ESRCH,3 + .e EINTR,4 + .e EIO,5 + .e ENXIO,6 + .e E2BIG,7 + .e ENOEXEC,8 + .e EBADF,9 + .e ECHILD,10 + .e EAGAIN,11 + .e ENOMEM,12 + .e EACCES,13 + .e EFAULT,14 + .e ENOTBLK,15 + .e EBUSY,16 + .e EEXIST,17 + .e EXDEV,18 + .e ENODEV,19 + .e ENOTDIR,20 + .e EISDIR,21 + .e EINVAL,22 + .e ENFILE,23 + .e EMFILE,24 + .e ENOTTY,25 + .e ETXTBSY,26 + .e EFBIG,27 + .e ENOSPC,28 + .e ESPIPE,29 + .e EROFS,30 + .e EMLINK,31 + .e EPIPE,32 + .e EDOM,33 + .e ERANGE,34 + .e EDEADLK,35 + .e ENAMETOOLONG,36 + .e ENOLCK,37 + .e ENOSYS,38 + .e ENOTEMPTY,39 + .e ELOOP,40 + .e ENOMSG,42 + .e EIDRM,43 + .e EUSERS,87 + .e ENOTSOCK,88 + .e EDESTADDRREQ,89 + .e EMSGSIZE,90 + .e EPROTOTYPE,91 + .e ENOPROTOOPT,92 + .e EPROTONOSUPPORT,93 + .e ESOCKTNOSUPPORT,94 + .e EOPNOTSUPP,95 + .e EPFNOSUPPORT,96 + .e EAFNOSUPPORT,97 + .e EADDRINUSE,98 + .e EADDRNOTAVAIL,99 + .e EL2NSYNC,45 + .e EL3HLT,46 + .e EL3RST,47 + .e ELNRNG,48 + .e ETIME,62 + .e ENONET,64 + .e EREMOTE,66 + .e EPROTO,71 + .e EBADMSG,74 + .e EOVERFLOW,75 + .e EILSEQ,84 + .e ERESTART,85 + .e ENETDOWN,100 + .e ENETUNREACH,101 + .e ENETRESET,102 + .e ECONNABORTED,103 + .e ECONNRESET,104 + .e ENOBUFS,105 + .e EISCONN,106 + .e ENOTCONN,107 + .e ESHUTDOWN,108 + .e ETOOMANYREFS,109 + .e ETIMEDOUT,110 + .e ECONNREFUSED,111 + .e EHOSTDOWN,112 + .e EHOSTUNREACH,113 + .e EALREADY,114 + .e EINPROGRESS,115 + .e ESTALE,116 + .e EDQUOT,122 + .e ECANCELED,125 + .e EOWNERDEAD,130 + .e ENOTRECOVERABLE,131 .long 0 .byte 0 .endobj kLinuxErrnos,globl diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index c47eb24ab..30c9d8f48 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -213,6 +213,15 @@ "scp $f $f.dbg win10:" "ssh win10 ./%s.com")) mode name (file-name-nondirectory name))) + ((eq kind 'run-xnu) + (format + (cosmo-join + " && " + `("m=%s; f=o/$m/%s.com" + ,(concat "make -j12 -O $f MODE=$m") + "scp $f $f.dbg xnu:" + "ssh xnu ./%s.com")) + mode name (file-name-nondirectory name))) ((and (equal suffix "") (cosmo-contains "_test." (buffer-file-name))) (format "m=%s; make -j12 -O MODE=$m %s" diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 11ef20ff8..398a11297 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -96,7 +96,7 @@ */ struct UnixErrno { - int errno; + int errno_; int winerr; const char *call; }; @@ -185,7 +185,7 @@ static int SysretErrno(lua_State *L, const char *call, int olderr) { lua_pushnil(L); ep = lua_newuserdatauv(L, sizeof(*ep), 1); luaL_setmetatable(L, "unix.Errno"); - ep->errno = unixerr; + ep->errno_ = unixerr; ep->winerr = winerr; ep->call = call; errno = olderr; @@ -2047,7 +2047,7 @@ static struct UnixErrno *GetUnixErrno(lua_State *L) { // unix.Errno:errno() // └─→ errno:int static int LuaUnixErrnoErrno(lua_State *L) { - return ReturnInteger(L, GetUnixErrno(L)->errno); + return ReturnInteger(L, GetUnixErrno(L)->errno_); } static int LuaUnixErrnoWinerr(lua_State *L) { @@ -2055,11 +2055,11 @@ static int LuaUnixErrnoWinerr(lua_State *L) { } static int LuaUnixErrnoName(lua_State *L) { - return ReturnString(L, strerrno(GetUnixErrno(L)->errno)); + return ReturnString(L, strerrno(GetUnixErrno(L)->errno_)); } static int LuaUnixErrnoDoc(lua_State *L) { - return ReturnString(L, strerdoc(GetUnixErrno(L)->errno)); + return ReturnString(L, strerdoc(GetUnixErrno(L)->errno_)); } static int LuaUnixErrnoCall(lua_State *L) { @@ -2071,10 +2071,10 @@ static int LuaUnixErrnoToString(lua_State *L) { struct UnixErrno *e; e = GetUnixErrno(L); if (e->call) { - strerror_wr(e->errno, e->winerr, msg, sizeof(msg)); + strerror_wr(e->errno_, e->winerr, msg, sizeof(msg)); lua_pushfstring(L, "%s() failed: %s", e->call, msg); } else { - lua_pushstring(L, strerrno(e->errno)); + lua_pushstring(L, strerrno(e->errno_)); } return 1; } diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 15ffe1f82..47390cec3 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -5404,7 +5404,7 @@ static void LogClose(const char *reason) { INFOF("(stat) %s %s with %,ld unprocessed and %,d handled (%,d workers)", DescribeClient(), reason, amtread, messageshandled, shared->workers); } else { - DEBUGF("(stat) %s %s with %,d requests handled", DescribeClient(), reason, + DEBUGF("(stat) %s %s with %,d messages handled", DescribeClient(), reason, messageshandled); } } @@ -6175,8 +6175,6 @@ static bool HandleMessageActual(void) { p = stpcpy(p, referrerpolicy); p = stpcpy(p, "\r\n"); } - LockInc(&shared->c.messageshandled); - ++messageshandled; if (loglatency || LOGGABLE(kLogDebug)) { now = nowl(); LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs", From 59b6ae1cbd5fc21ca3cf0f1de783d28dca6cdb21 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 16 May 2022 14:33:19 -0700 Subject: [PATCH 07/40] Add NET_HTTP to the amalgamation This also fixes some POSIX header warnings. --- Makefile | 2 + examples/greenbean.c | 45 ++++++------ libc/str/classifypath.c | 42 +++++------ libc/str/isabspath.c | 2 +- libc/str/path.h | 12 ++-- libc/sysv/consts/_posix2.h | 34 --------- test/libc/str/classifypath_test.c | 116 +++++++++++++++--------------- 7 files changed, 108 insertions(+), 145 deletions(-) delete mode 100644 libc/sysv/consts/_posix2.h diff --git a/Makefile b/Makefile index 44d406136..f2282e120 100644 --- a/Makefile +++ b/Makefile @@ -258,6 +258,7 @@ loc: o/$(MODE)/tool/build/summy.com $(XARGS) wc -l | grep total | awk '{print $$1}' | $< COSMOPOLITAN_OBJECTS = \ + NET_HTTP \ LIBC_DNS \ LIBC_SOCK \ LIBC_NT_WS2_32 \ @@ -331,6 +332,7 @@ COSMOPOLITAN_HEADERS = \ LIBC_UNICODE \ LIBC_X \ LIBC_ZIPOS \ + NET_HTTP \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ diff --git a/examples/greenbean.c b/examples/greenbean.c index 58338fd69..fe5ac739c 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -1,21 +1,12 @@ -/*-*- 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. │ -╚─────────────────────────────────────────────────────────────────────────────*/ +#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/assert.h" #include "libc/bits/atomic.h" #include "libc/calls/calls.h" @@ -24,6 +15,7 @@ #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" #include "libc/dce.h" +#include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" @@ -31,7 +23,7 @@ #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/sock/goodsocket.internal.h" +#include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" @@ -93,7 +85,6 @@ */ #define PORT 8080 -#define THREADS 10000 #define HEARTBEAT 100 #define KEEPALIVE 5000 #define LOGGING 0 @@ -135,7 +126,7 @@ int Worker(void *id) { goto WorkerFinished; } - listen(server, 10); + listen(server, 1); // connection loop while (!closingtime) { @@ -150,6 +141,8 @@ int Worker(void *id) { char inbuf[1500], outbuf[512], *p, *q; int clientip, client, inmsglen, outmsglen; + // this slows the server down a lot but is needed on non-Linux to + // react to keyboard ctrl-c if (!IsLinux() && poll(&(struct pollfd){server, POLLIN}, 1, HEARTBEAT) < 1) { continue; @@ -258,9 +251,9 @@ void OnCtrlC(int sig) { } int main(int argc, char *argv[]) { - int i; + int i, threads; uint32_t *hostips; - ShowCrashReports(); + // ShowCrashReports(); sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0); for (hostips = GetHostIps(), i = 0; hostips[i]; ++i) { kprintf("listening on http://%d.%d.%d.%d:%d\n", @@ -268,8 +261,10 @@ int main(int argc, char *argv[]) { (hostips[i] & 0x0000ff00) >> 010, (hostips[i] & 0x000000ff) >> 000, PORT); } - workers = THREADS; - for (i = 0; i < THREADS; ++i) { + threads = argc > 1 ? atoi(argv[1]) : 0; + if (!threads) threads = GetCpuCount(); + workers = threads; + for (i = 0; i < threads; ++i) { void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); clone(Worker, stack, 65536, diff --git a/libc/str/classifypath.c b/libc/str/classifypath.c index 9f26cc410..9213b93fa 100644 --- a/libc/str/classifypath.c +++ b/libc/str/classifypath.c @@ -36,11 +36,11 @@ * inclusive of DOS drive paths, DOS rooted paths, in addition to the * New Technology UNC paths, then you may do the following: * - * if (_classifypath(str) & _PATH_ABS) { ... } + * if (_classifypath(str) & _kPathAbs) { ... } * * To check if path is a relative path: * - * if (~_classifypath(str) & _PATH_ABS) { ... } + * if (~_classifypath(str) & _kPathAbs) { ... } * * Please note the above check includes rooted paths such as `\foo` * which is considered absolute by MSDN and we consider it absolute @@ -52,14 +52,14 @@ * * @return integer value that's one of following: * - `0` if non-weird relative path e.g. `c` - * - `_PATH_ABS` if absolute (or rooted dos) path e.g. `/⋯` - * - `_PATH_DOS` if `c:`, `d:foo` i.e. drive-relative path - * - `_PATH_ABS|_PATH_DOS` if proper dos path e.g. `c:/foo` - * - `_PATH_DOS|_PATH_DEV` if dos device path e.g. `nul`, `conin$` - * - `_PATH_ABS|_PATH_WIN` if `//c`, `//?c`, etc. - * - `_PATH_ABS|_PATH_WIN|_PATH_DEV` if `//./⋯`, `//?/⋯` - * - `_PATH_ABS|_PATH_WIN|_PATH_DEV|_PATH_ROOT` if `//.` or `//?` - * - `_PATH_ABS|_PATH_NT` e.g. `\??\\⋯` (undoc. strict backslash) + * - `_kPathAbs` if absolute (or rooted dos) path e.g. `/⋯` + * - `_kPathDos` if `c:`, `d:foo` i.e. drive-relative path + * - `_kPathAbs|_kPathDos` if proper dos path e.g. `c:/foo` + * - `_kPathDos|_kPathDev` if dos device path e.g. `nul`, `conin$` + * - `_kPathAbs|_kPathWin` if `//c`, `//?c`, etc. + * - `_kPathAbs|_kPathWin|_kPathDev` if `//./⋯`, `//?/⋯` + * - `_kPathAbs|_kPathWin|_kPathDev|_kPathRoot` if `//.` or `//?` + * - `_kPathAbs|_kPathNt` e.g. `\??\\⋯` (undoc. strict backslash) * @see "The Definitive Guide on Win32 to NT Path Conversion", James * Forshaw, Google Project Zero Blog, 2016-02-29 * @see "Naming Files, Paths, and Namespaces", MSDN 01/04/2021 @@ -94,17 +94,17 @@ int _classifypath(const char *s) { (s[2] == 'm' || s[2] == 'M'))) && // ('1' <= s[3] && s[3] <= '9') && // !s[4])) { - return _PATH_DOS | _PATH_DEV; + return _kPathDos | _kPathDev; } switch (s[1]) { case ':': switch (s[2]) { case 0: // c: default: // c:wut⋯ - return _PATH_DOS; + return _kPathDos; case '/': // c:/⋯ case '\\': // c:\⋯ - return _PATH_ABS | _PATH_DOS; + return _kPathAbs | _kPathDos; } default: return 0; @@ -113,37 +113,37 @@ int _classifypath(const char *s) { if (SupportsWindows()) { if (s[1] == '?' && s[2] == '?') { if (!s[3]) { - return _PATH_ABS | _PATH_NT | _PATH_ROOT; // \??\⋯ + return _kPathAbs | _kPathNt | _kPathRoot; // \??\⋯ } else if (s[3] == '\\') { - return _PATH_ABS | _PATH_NT; // \??\⋯ + return _kPathAbs | _kPathNt; // \??\⋯ } } } // fallthrough case '/': if (!SupportsWindows()) { - return _PATH_ABS; + return _kPathAbs; } switch (s[1]) { case 0: // / default: // /⋯ - return _PATH_ABS; + return _kPathAbs; case '/': case '\\': switch (s[2]) { case 0: // // default: // //⋯ - return _PATH_ABS | _PATH_WIN; + return _kPathAbs | _kPathWin; case '.': case '?': switch (s[3]) { case 0: // //? or //. - return _PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT; + return _kPathAbs | _kPathWin | _kPathDev | _kPathRoot; default: // //?⋯ or //.⋯ - return _PATH_ABS | _PATH_WIN; + return _kPathAbs | _kPathWin; case '/': case '\\': // //?/⋯ or //./⋯ - return _PATH_ABS | _PATH_WIN | _PATH_DEV; + return _kPathAbs | _kPathWin | _kPathDev; } } } diff --git a/libc/str/isabspath.c b/libc/str/isabspath.c index 4e40be341..60de5b76a 100644 --- a/libc/str/isabspath.c +++ b/libc/str/isabspath.c @@ -32,5 +32,5 @@ * */ bool _isabspath(const char *path) { - return _classifypath(path) & _PATH_ABS; + return _classifypath(path) & _kPathAbs; } diff --git a/libc/str/path.h b/libc/str/path.h index 10b1fca62..74c65a91f 100644 --- a/libc/str/path.h +++ b/libc/str/path.h @@ -1,12 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_STR_PATH_H_ #define COSMOPOLITAN_LIBC_STR_PATH_H_ -#define _PATH_ABS 1 -#define _PATH_DEV 2 -#define _PATH_ROOT 4 -#define _PATH_DOS 8 -#define _PATH_WIN 16 -#define _PATH_NT 32 +#define _kPathAbs 1 +#define _kPathDev 2 +#define _kPathRoot 4 +#define _kPathDos 8 +#define _kPathWin 16 +#define _kPathNt 32 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/sysv/consts/_posix2.h b/libc/sysv/consts/_posix2.h deleted file mode 100644 index 9a0a08200..000000000 --- a/libc/sysv/consts/_posix2.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX2_H_ -#define COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX2_H_ -#include "libc/runtime/symbolic.h" - -#define _POSIX2_BC_BASE_MAX SYMBOLIC(_POSIX2_BC_BASE_MAX) -#define _POSIX2_BC_DIM_MAX SYMBOLIC(_POSIX2_BC_DIM_MAX) -#define _POSIX2_BC_SCALE_MAX SYMBOLIC(_POSIX2_BC_SCALE_MAX) -#define _POSIX2_BC_STRING_MAX SYMBOLIC(_POSIX2_BC_STRING_MAX) -#define _POSIX2_CHARCLASS_NAME_MAX SYMBOLIC(_POSIX2_CHARCLASS_NAME_MAX) -#define _POSIX2_COLL_WEIGHTS_MAX SYMBOLIC(_POSIX2_COLL_WEIGHTS_MAX) -#define _POSIX2_C_BIND SYMBOLIC(_POSIX2_C_BIND) -#define _POSIX2_EXPR_NEST_MAX SYMBOLIC(_POSIX2_EXPR_NEST_MAX) -#define _POSIX2_LINE_MAX SYMBOLIC(_POSIX2_LINE_MAX) -#define _POSIX2_RE_DUP_MAX SYMBOLIC(_POSIX2_RE_DUP_MAX) -#define _POSIX2_VERSION SYMBOLIC(_POSIX2_VERSION) - -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern const long _POSIX2_BC_BASE_MAX; -extern const long _POSIX2_BC_DIM_MAX; -extern const long _POSIX2_BC_SCALE_MAX; -extern const long _POSIX2_BC_STRING_MAX; -extern const long _POSIX2_CHARCLASS_NAME_MAX; -extern const long _POSIX2_COLL_WEIGHTS_MAX; -extern const long _POSIX2_C_BIND; -extern const long _POSIX2_EXPR_NEST_MAX; -extern const long _POSIX2_LINE_MAX; -extern const long _POSIX2_RE_DUP_MAX; -extern const long _POSIX2_VERSION; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX2_H_ */ diff --git a/test/libc/str/classifypath_test.c b/test/libc/str/classifypath_test.c index 174ae9172..666ab280a 100644 --- a/test/libc/str/classifypath_test.c +++ b/test/libc/str/classifypath_test.c @@ -50,72 +50,72 @@ TEST(_classifypath, test) { if (!SupportsWindows()) return; EXPECT_EQ(0, _classifypath("")); EXPECT_EQ(0, _classifypath("xyz")); - EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("CON")); - EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("NUL")); + EXPECT_EQ(_kPathDos | _kPathDev, _classifypath("CON")); + EXPECT_EQ(_kPathDos | _kPathDev, _classifypath("NUL")); EXPECT_EQ(0, _classifypath(":")); - EXPECT_EQ(_PATH_DOS, _classifypath("::")); - EXPECT_EQ(_PATH_DOS, _classifypath(":::")); - EXPECT_EQ(_PATH_DOS, _classifypath("::::")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("::\\")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\:")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\C:")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\C:\\")); - EXPECT_EQ(_PATH_ABS, _classifypath("/")); - EXPECT_EQ(_PATH_ABS, _classifypath("/:")); - EXPECT_EQ(_PATH_ABS, _classifypath("/C:")); - EXPECT_EQ(_PATH_ABS, _classifypath("/C:/")); + EXPECT_EQ(_kPathDos, _classifypath("::")); + EXPECT_EQ(_kPathDos, _classifypath(":::")); + EXPECT_EQ(_kPathDos, _classifypath("::::")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("::\\")); + EXPECT_EQ(_kPathAbs, _classifypath("\\")); + EXPECT_EQ(_kPathAbs, _classifypath("\\:")); + EXPECT_EQ(_kPathAbs, _classifypath("\\C:")); + EXPECT_EQ(_kPathAbs, _classifypath("\\C:\\")); + EXPECT_EQ(_kPathAbs, _classifypath("/")); + EXPECT_EQ(_kPathAbs, _classifypath("/:")); + EXPECT_EQ(_kPathAbs, _classifypath("/C:")); + EXPECT_EQ(_kPathAbs, _classifypath("/C:/")); EXPECT_EQ(0, _classifypath("C")); - EXPECT_EQ(_PATH_DOS, _classifypath("C:")); - EXPECT_EQ(_PATH_DOS, _classifypath("C:a")); - EXPECT_EQ(_PATH_DOS, _classifypath("C:a\\")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\a")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/a")); - EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\;")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f")); - EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\")); - EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC")); - EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC\\")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\?")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC")); - EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\?\\UNC\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathDos, _classifypath("C:")); + EXPECT_EQ(_kPathDos, _classifypath("C:a")); + EXPECT_EQ(_kPathDos, _classifypath("C:a\\")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:/")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\a")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:/a")); + EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\;")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\b\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\b")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f")); + EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\")); + EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\UNC")); + EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\UNC\\")); + EXPECT_EQ(_kPathAbs, _classifypath("\\?")); + EXPECT_EQ(_kPathAbs, _classifypath("\\?\\")); + EXPECT_EQ(_kPathAbs, _classifypath("\\?\\UNC")); + EXPECT_EQ(_kPathAbs, _classifypath("\\?\\UNC\\")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\?\\UNC\\")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("\\\\?")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\C:\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??\\C:\\")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("\\\\.")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\C:\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\/")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("/\\")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("///")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//;")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\.\\")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\.\\C:\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\/")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("/\\")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("///")); + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//;")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("//?")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("/\\?")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("\\/?")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//??")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//??")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("//.")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("\\/.")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, _classifypath("/\\.")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./")); - EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./C:/")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("//./")); + EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("//./C:/")); } From 8bfb70ca3f9c0add016dd41b96d9ae58e7f17c3d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 16 May 2022 16:49:20 -0700 Subject: [PATCH 08/40] Add Compress() and Uncompress() to redbean --- libc/fmt/leb128.h | 3 +- libc/fmt/uleb64.c | 2 +- libc/fmt/unuleb64.c | 41 +++++++++++++++++++++ test/libc/fmt/uleb64_test.c | 72 +++++++++++++++++++++++++++++++++++++ tool/net/help.txt | 33 +++++++++++++++++ tool/net/lfuncs.c | 53 +++++++++++++++++++++++++-- tool/net/lfuncs.h | 5 +++ tool/net/lunix.c | 30 ++++++---------- tool/net/redbean.c | 2 ++ 9 files changed, 218 insertions(+), 23 deletions(-) create mode 100644 libc/fmt/unuleb64.c create mode 100644 test/libc/fmt/uleb64_test.c diff --git a/libc/fmt/leb128.h b/libc/fmt/leb128.h index 2c3419585..25b92c4f9 100644 --- a/libc/fmt/leb128.h +++ b/libc/fmt/leb128.h @@ -5,8 +5,9 @@ COSMOPOLITAN_C_START_ char *sleb64(char *, int64_t); char *zleb64(char *, int64_t); -char *uleb64(char *, uint64_t); +char *uleb64(char[hasatleast 10], uint64_t); int unzleb64(const char *, size_t, int64_t *); +int unuleb64(char *, size_t, uint64_t *); #ifndef __STRICT_ANSI__ char *sleb128(char *, int128_t); diff --git a/libc/fmt/uleb64.c b/libc/fmt/uleb64.c index c4d6561dd..01f798490 100644 --- a/libc/fmt/uleb64.c +++ b/libc/fmt/uleb64.c @@ -36,7 +36,7 @@ * @param x is number * @return p + i */ -char *uleb64(char *p, uint64_t x) { +char *uleb64(char p[hasatleast 10], uint64_t x) { int c; for (;;) { c = x & 127; diff --git a/libc/fmt/unuleb64.c b/libc/fmt/unuleb64.c new file mode 100644 index 000000000..80c83a771 --- /dev/null +++ b/libc/fmt/unuleb64.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 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/fmt/leb128.h" + +/** + * Decodes unsigned integer from array. + * + * @param p is input array + * @param n is capacity of p + * @param x receives number number + * @return bytes decoded or -1 on error + */ +int unuleb64(char *p, size_t n, uint64_t *x) { + int k; + size_t i; + uint64_t t; + for (k = t = i = 0; i < n; ++i, k += 7) { + t |= (uint64_t)(p[i] & 127) << k; + if (~p[i] & 128) { + *x = t; + return i + 1; + } + } + return -1; +} diff --git a/test/libc/fmt/uleb64_test.c b/test/libc/fmt/uleb64_test.c new file mode 100644 index 000000000..e88fa40ee --- /dev/null +++ b/test/libc/fmt/uleb64_test.c @@ -0,0 +1,72 @@ +/*-*- 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/fmt/leb128.h" +#include "libc/limits.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/testlib.h" + +char p[10]; +uint64_t x; + +TEST(uleb64, testZero) { + EXPECT_EQ(1, uleb64(p, 0) - p); + EXPECT_EQ(0, p[0]); + EXPECT_EQ(1, unuleb64(p, 10, &x)); + EXPECT_EQ(0, x); + EXPECT_EQ(-1, unuleb64(p, 0, &x)); +} + +TEST(uleb64, testOne) { + EXPECT_EQ(1, uleb64(p, 1) - p); + EXPECT_EQ(1, p[0]); + EXPECT_EQ(1, unuleb64(p, 10, &x)); + EXPECT_EQ(1, x); +} + +TEST(uleb64, test255) { + EXPECT_EQ(2, uleb64(p, 255) - p); + EXPECT_EQ(255, p[0] & 255); + EXPECT_EQ(1, p[1]); + EXPECT_EQ(2, unuleb64(p, 10, &x)); + EXPECT_EQ(255, x); +} + +TEST(uleb64, testFFFF) { + EXPECT_EQ(3, uleb64(p, 0xFFFF) - p); + EXPECT_EQ(255, p[0] & 255); + EXPECT_EQ(255, p[1] & 255); + EXPECT_EQ(3, p[2] & 255); +} + +TEST(uleb64, testMax) { + EXPECT_EQ(10, uleb64(p, UINT64_MAX) - p); + EXPECT_EQ(255, p[0x00] & 255); + EXPECT_EQ(255, p[0x01] & 255); + EXPECT_EQ(255, p[0x02] & 255); + EXPECT_EQ(255, p[0x03] & 255); + EXPECT_EQ(255, p[0x04] & 255); + EXPECT_EQ(255, p[0x05] & 255); + EXPECT_EQ(255, p[0x06] & 255); + EXPECT_EQ(255, p[0x07] & 255); + EXPECT_EQ(255, p[0x08] & 255); + EXPECT_EQ(001, p[0x09] & 255); + EXPECT_EQ(10, unuleb64(p, 10, &x)); + EXPECT_EQ(UINT64_MAX, x); + EXPECT_EQ(-1, unuleb64(p, 7, &x)); +} diff --git a/tool/net/help.txt b/tool/net/help.txt index f31c9264e..2bad38c48 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1308,6 +1308,39 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. + Compress(uncompdata:str[, level:int]) → compdata:str + + Compresses data using DEFLATE algorithm. The compression + format here is defined to be quick and handy for things like + database fields. For example: + + >: Compress('hello') + "\x05\x86\xa6\x106x\x9c\xcbH\xcd\xc9\xc9\x07\x00\x06,\x02\x15" + >: Uncompress(Compress('hello')) + "hello" + + `level` is the compression level, which defaults to 7. The max + is 10. Lower numbers go faster. Higher numbers go slower, but + have better compression ratios. + + [implementation details] + The binary wire format is defined as follows: + + 1. uleb64 uncompressed byte size (1 to 10 bytes) + 2. uint32_t crc32 (4 bytes; zlib polynomial) + 3. data (created by zlib compress function) + + Uncompress(compdata:str) → uncompdata:str + + Uncompresses data using DEFLATE algorithm. This applies the + inverse transform of the Compress() function. See its docs for + further details on usage and encoding. + + This function throws exceptions in the event that the value + couldn't be decoded. There's a crc32 check to make our check + of validity iron-clad. It's implemented using Intel CLMUL so + it has ludicrous speed performance as well. + Benchmark(func[, count[, maxattempts]]) └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 503fb899f..0304038e8 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -17,10 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/scale/cdecimate2xuint8x8.h" +#include "libc/bits/bits.h" #include "libc/bits/popcnt.h" #include "libc/calls/calls.h" #include "libc/calls/struct/rusage.h" #include "libc/fmt/itoa.h" +#include "libc/fmt/leb128.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -55,6 +57,7 @@ #include "third_party/mbedtls/sha1.h" #include "third_party/mbedtls/sha256.h" #include "third_party/mbedtls/sha512.h" +#include "third_party/zlib/zlib.h" #include "tool/net/lfuncs.h" static int Rdpid(void) { @@ -155,7 +158,7 @@ int LuaDecimate(lua_State *L) { unsigned char *p; s = luaL_checklstring(L, 1, &n); m = ROUNDUP(n, 16); - p = xmalloc(m); + CHECK_NOTNULL((p = LuaAlloc(L, m))); bzero(p + n, m - n); cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); lua_pushlstring(L, (char *)p, (n + 1) >> 1); @@ -405,7 +408,7 @@ int LuaGetRandomBytes(lua_State *L) { luaL_argerror(L, 1, "not in range 1..256"); unreachable; } - p = xmalloc(n); + CHECK_NOTNULL((p = LuaAlloc(L, n))); CHECK_EQ(n, getrandom(p, n, 0)); lua_pushlstring(L, p, n); free(p); @@ -678,3 +681,49 @@ int LuaBenchmark(lua_State *L) { lua_pushinteger(L, attempts); return 4; } + +int LuaCompress(lua_State *L) { + size_t n, m; + char *q, *e; + uint32_t crc; + const char *p; + int level, hdrlen; + p = luaL_checklstring(L, 1, &n); + level = luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + m = compressBound(n); + CHECK_NOTNULL((q = LuaAlloc(L, 10 + 4 + m))); + crc = crc32_z(0, p, n); + e = uleb64(q, n); + e = WRITE32LE(e, crc); + hdrlen = e - q; + CHECK_EQ(Z_OK, compress2((unsigned char *)(q + hdrlen), &m, + (unsigned char *)p, n, level)); + lua_pushlstring(L, q, hdrlen + m); + free(q); + return 1; +} + +int LuaUncompress(lua_State *L) { + char *q; + uint32_t crc; + int rc, level; + const char *p; + size_t n, m, len; + p = luaL_checklstring(L, 1, &n); + if ((rc = unuleb64(p, n, &m)) == -1 || n < rc + 4) { + luaL_error(L, "compressed value too short to be valid"); + unreachable; + } + len = m; + crc = READ32LE(p + rc); + CHECK_NOTNULL((q = LuaAlloc(L, m))); + if (uncompress((void *)q, &m, (unsigned char *)p + rc + 4, n) != Z_OK || + m != len || crc32_z(0, q, m) != crc) { + free(q); + luaL_error(L, "compressed value is corrupted"); + unreachable; + } + lua_pushlstring(L, q, m); + free(q); + return 1; +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index 301c8a49e..816081929 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -11,11 +11,15 @@ int LuaUnix(lua_State *); int luaopen_argon2(lua_State *); int luaopen_lsqlite3(lua_State *); +void *LuaRealloc(lua_State *, void *, size_t); +void *LuaAlloc(lua_State *, size_t); + int LuaBenchmark(lua_State *); int LuaBin(lua_State *); int LuaBsf(lua_State *); int LuaBsr(lua_State *); int LuaCategorizeIp(lua_State *); +int LuaCompress(lua_State *); int LuaCrc32(lua_State *); int LuaCrc32c(lua_State *); int LuaDecimate(lua_State *); @@ -78,6 +82,7 @@ int LuaSha384(lua_State *); int LuaSha512(lua_State *); int LuaSleep(lua_State *); int LuaSlurp(lua_State *); +int LuaUncompress(lua_State *); int LuaUnderlong(lua_State *); int LuaVisualizeControlCodes(lua_State *); diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 398a11297..f3c175dc7 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -103,7 +103,7 @@ struct UnixErrno { static lua_State *GL; -static void *LuaUnixRealloc(lua_State *L, void *p, size_t n) { +void *LuaRealloc(lua_State *L, void *p, size_t n) { void *p2; if ((p2 = realloc(p, n))) { return p2; @@ -116,16 +116,8 @@ static void *LuaUnixRealloc(lua_State *L, void *p, size_t n) { return p2; } -static void *LuaUnixAllocRaw(lua_State *L, size_t n) { - return LuaUnixRealloc(L, 0, n); -} - -static void *LuaUnixAlloc(lua_State *L, size_t n) { - void *p; - if ((p = LuaUnixAllocRaw(L, n))) { - bzero(p, n); - } - return p; +void *LuaAlloc(lua_State *L, size_t n) { + return LuaRealloc(L, 0, n); } static lua_Integer FixLimit(long x) { @@ -241,7 +233,7 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { lua_len(L, i); n = lua_tointeger(L, -1); lua_pop(L, 1); - if ((p = LuaUnixAllocRaw(L, (n + 1) * sizeof(*p)))) { + if ((p = LuaAlloc(L, (n + 1) * sizeof(*p)))) { for (j = 1; j <= n; ++j) { lua_geti(L, i, j); s = strdup(lua_tostring(L, -1)); @@ -442,7 +434,7 @@ static int LuaUnixReadlink(lua_State *L) { size_t got, bufsiz = 8192; path = luaL_checkstring(L, 1); dirfd = luaL_optinteger(L, 2, AT_FDCWD); - if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((buf = LuaAlloc(L, bufsiz))) { if ((rc = readlinkat(dirfd, path, buf, bufsiz)) != -1) { got = rc; if (got < bufsiz) { @@ -543,7 +535,7 @@ static int LuaUnixCommandv(lua_State *L) { char *pathbuf, *resolved; olderr = errno; prog = luaL_checkstring(L, 1); - if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX))) { + if ((pathbuf = LuaAlloc(L, PATH_MAX))) { if ((resolved = commandv(prog, pathbuf, PATH_MAX))) { lua_pushstring(L, resolved); free(pathbuf); @@ -913,7 +905,7 @@ static int LuaUnixRead(lua_State *L) { bufsiz = luaL_optinteger(L, 2, BUFSIZ); offset = luaL_optinteger(L, 3, -1); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((buf = LuaAlloc(L, bufsiz))) { if (offset == -1) { rc = read(fd, buf, bufsiz); } else { @@ -1246,7 +1238,7 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifreq *ifr; struct ifconf conf; olderr = errno; - if (!(data = LuaUnixAllocRaw(L, (n = 4096)))) { + if (!(data = LuaAlloc(L, (n = 4096)))) { return SysretErrno(L, "siocgifconf", olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { @@ -1345,7 +1337,7 @@ static int LuaUnixPoll(lua_State *L) { lua_pushnil(L); for (fds = 0, nfds = 0; lua_next(L, 1);) { if (lua_isinteger(L, -2)) { - if ((fds2 = LuaUnixRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) { + if ((fds2 = LuaRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) { fds2[nfds].fd = lua_tointeger(L, -2); fds2[nfds].events = lua_tointeger(L, -1); fds = fds2; @@ -1392,7 +1384,7 @@ static int LuaUnixRecvfrom(lua_State *L) { bufsiz = luaL_optinteger(L, 2, 1500); bufsiz = MIN(bufsiz, 0x7ffff000); flags = luaL_optinteger(L, 3, 0); - if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((buf = LuaAlloc(L, bufsiz))) { rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); if (rc != -1) { got = rc; @@ -1423,7 +1415,7 @@ static int LuaUnixRecv(lua_State *L) { bufsiz = luaL_optinteger(L, 2, 1500); bufsiz = MIN(bufsiz, 0x7ffff000); flags = luaL_optinteger(L, 3, 0); - if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((buf = LuaAlloc(L, bufsiz))) { rc = recv(fd, buf, bufsiz, flags); if (rc != -1) { got = rc; diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 47390cec3..074bc577b 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -5029,6 +5029,7 @@ static const luaL_Reg kLuaFuncs[] = { {"Bsf", LuaBsf}, // {"Bsr", LuaBsr}, // {"CategorizeIp", LuaCategorizeIp}, // + {"Compress", LuaCompress}, // {"Crc32", LuaCrc32}, // {"Crc32c", LuaCrc32c}, // {"Decimate", LuaDecimate}, // @@ -5164,6 +5165,7 @@ static const luaL_Reg kLuaFuncs[] = { {"Sleep", LuaSleep}, // {"Slurp", LuaSlurp}, // {"StoreAsset", LuaStoreAsset}, // + {"Uncompress", LuaUncompress}, // {"Underlong", LuaUnderlong}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, // {"Write", LuaWrite}, // From ce716771568fc9748575909133275537c3a53210 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 04:14:28 -0700 Subject: [PATCH 09/40] Improve threading support further --- examples/greenbean.c | 10 +- libc/calls/close.c | 6 +- libc/calls/dup-nt.c | 28 +- libc/calls/g_sighandrvas.c | 2 +- libc/calls/internal.h | 7 +- libc/calls/interrupts-nt.c | 7 +- libc/calls/open-nt.c | 20 +- libc/calls/pipe-nt.c | 18 +- libc/calls/poll-nt.c | 2 +- libc/calls/reservefd.c | 55 ++- libc/calls/sigaction.c | 4 +- libc/calls/sigchld-nt.c | 2 +- libc/calls/wait4-nt.c | 12 +- libc/intrin/asan.c | 39 +- libc/intrin/asan.internal.h | 2 - libc/intrin/cxaatexit.c | 6 + libc/intrin/exit1.greg.c | 3 +- libc/intrin/g_fds.c | 2 +- libc/intrin/gettid.greg.c | 2 +- libc/intrin/kprintf.greg.c | 2 +- libc/intrin/releasefd.c | 8 +- libc/intrin/spinlock.h | 52 +- libc/intrin/threaded.h | 16 + libc/intrin/threaded.internal.h | 11 - libc/intrin/tls.greg.c | 111 ++--- libc/intrin/tls.h | 13 - libc/intrin/winthread.internal.h | 24 - libc/log/vflogf.c | 2 +- libc/nexgen32e/hastls.c | 21 - libc/nexgen32e/nexgen32e.mk | 1 - libc/nexgen32e/threaded.c | 4 +- libc/nt/kernel32/TlsAlloc.s | 4 +- libc/nt/kernel32/TlsFree.s | 4 +- libc/nt/kernel32/TlsGetValue.s | 4 +- libc/nt/kernel32/TlsSetValue.s | 4 +- libc/nt/master.sh | 10 +- libc/nt/thread.h | 5 + libc/runtime/clone.greg.c | 248 ++++------ libc/runtime/getsymboltable.greg.c | 4 + libc/runtime/memtrack.internal.h | 2 +- libc/sock/closesocket-nt.c | 3 + libc/sock/kntwsadata.c | 7 +- libc/sock/socket-nt.c | 3 + libc/sock/socketpair-nt.c | 5 + libc/stdio/fflush.internal.h | 2 +- libc/stdio/stdio.h | 2 +- libc/sysv/errno.greg.c | 40 +- libc/testlib/testmain.c | 1 + libc/time/localtime.c | 2 +- .../libc/intrin/tls_test.c | 31 +- test/libc/rand/rand64_test.c | 9 +- test/libc/runtime/clone_test.c | 45 +- third_party/chibicc/as.c | 2 + third_party/chibicc/chibicc.h | 116 ++--- third_party/chibicc/codegen.c | 30 ++ third_party/chibicc/kw.gperf | 5 +- third_party/chibicc/kw.h | 3 + third_party/chibicc/kw.inc | 453 +++++++++--------- third_party/chibicc/parse.c | 37 +- third_party/chibicc/test/spinlock_test.c | 54 +++ third_party/dlmalloc/dlmalloc.greg.c | 2 +- 61 files changed, 882 insertions(+), 747 deletions(-) create mode 100644 libc/intrin/threaded.h delete mode 100644 libc/intrin/threaded.internal.h delete mode 100644 libc/intrin/tls.h delete mode 100644 libc/intrin/winthread.internal.h delete mode 100644 libc/nexgen32e/hastls.c rename libc/intrin/winthread.c => test/libc/intrin/tls_test.c (81%) diff --git a/examples/greenbean.c b/examples/greenbean.c index fe5ac739c..932cb1560 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -18,6 +18,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/threaded.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -120,6 +121,7 @@ int Worker(void *id) { setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)); setsockopt(server, SOL_TCP, TCP_FASTOPEN, &yes, sizeof(yes)); setsockopt(server, SOL_TCP, TCP_QUICKACK, &yes, sizeof(yes)); + errno = 0; if (bind(server, &addr, sizeof(addr)) == -1) { if (LOGGING) kprintf("%s() failed %m\n", "socket"); @@ -265,11 +267,13 @@ int main(int argc, char *argv[]) { if (!threads) threads = GetCpuCount(); workers = threads; for (i = 0; i < threads; ++i) { + char *tls = __initialize_tls(malloc(64)); void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); - clone(Worker, stack, 65536, - CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, - (void *)(intptr_t)i, 0, 0, 0, 0); + CHECK_NE(-1, clone(Worker, stack, 65536, + CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS, + (void *)(intptr_t)i, 0, tls, 64, 0)); } status = ""; while (workers) { diff --git a/libc/calls/close.c b/libc/calls/close.c index 13cfe7b21..ef1a248fe 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -20,6 +20,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" @@ -46,6 +48,7 @@ */ int close(int fd) { int rc; + _spinlock(&__fds_lock); if (fd == -1) { rc = 0; } else if (fd < 0) { @@ -74,9 +77,10 @@ int close(int fd) { } } if (!__vforked) { - __releasefd(fd); + __releasefd_unlocked(fd); } } + _spunlock(&__fds_lock); STRACE("%s(%d) → %d% m", "close", fd, rc); return rc; } diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index ba52b9e20..865e28025 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -20,6 +20,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" @@ -32,25 +33,37 @@ * Implements dup(), dup2(), dup3(), and F_DUPFD for Windows. */ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { - int64_t proc, handle; + int64_t rc, proc, handle; // validate the api usage if (oldfd < 0) return einval(); if (flags & ~O_CLOEXEC) return einval(); + + _spinlock(&__fds_lock); + if (oldfd >= g_fds.n || (g_fds.p[oldfd].kind != kFdFile && g_fds.p[oldfd].kind != kFdSocket && g_fds.p[oldfd].kind != kFdConsole)) { + _spunlock(&__fds_lock); return ebadf(); } // allocate a new file descriptor if (newfd == -1) { - if ((newfd = __reservefd(start)) == -1) { + if ((newfd = __reservefd_unlocked(start)) == -1) { + _spunlock(&__fds_lock); return -1; } } else { - if (__ensurefds(newfd) == -1) return -1; - if (g_fds.p[newfd].kind) close(newfd); + if (__ensurefds_unlocked(newfd) == -1) { + _spunlock(&__fds_lock); + return -1; + } + if (g_fds.p[newfd].kind) { + _spunlock(&__fds_lock); + close(newfd); + _spinlock(&__fds_lock); + } g_fds.p[newfd].kind = kFdReserved; } @@ -80,9 +93,12 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { if (g_fds.p[oldfd].worker) { g_fds.p[newfd].worker = weaken(RefNtStdinWorker)(g_fds.p[oldfd].worker); } - return newfd; + rc = newfd; } else { __releasefd(newfd); - return __winerr(); + rc = __winerr(); } + + _spunlock(&__fds_lock); + return rc; } diff --git a/libc/calls/g_sighandrvas.c b/libc/calls/g_sighandrvas.c index 97964a95d..e7b259cb7 100644 --- a/libc/calls/g_sighandrvas.c +++ b/libc/calls/g_sighandrvas.c @@ -18,6 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -_Alignas(64) char __sig_lock; +_Alignas(64) int __sig_lock; unsigned __sighandrvas[NSIG]; unsigned __sighandflags[NSIG]; diff --git a/libc/calls/internal.h b/libc/calls/internal.h index e656c947b..5c4457c67 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -76,8 +76,8 @@ struct Fds { extern const struct Fd kEmptyFd; hidden extern int __vforked; -hidden extern char __fds_lock; -hidden extern char __sig_lock; +hidden extern int __fds_lock; +hidden extern int __sig_lock; hidden extern bool __time_critical; hidden extern unsigned __sighandrvas[NSIG]; hidden extern unsigned __sighandflags[NSIG]; @@ -85,8 +85,11 @@ hidden extern struct Fds g_fds; hidden extern const struct NtSecurityAttributes kNtIsInheritable; int __reservefd(int) hidden; +int __reservefd_unlocked(int) hidden; void __releasefd(int) hidden; +void __releasefd_unlocked(int) hidden; int __ensurefds(int) hidden; +int __ensurefds_unlocked(int) hidden; int64_t __getfdhandleactual(int) hidden; void __printfds(void) hidden; diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index a77b28bfd..b64e78198 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -23,11 +23,16 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" +#include "libc/intrin/spinlock.h" textwindows bool _check_interrupts(bool restartable, struct Fd *fd) { + bool res; if (__time_critical) return false; + if (_trylock(&__fds_lock)) return false; if (weaken(_check_sigalrm)) weaken(_check_sigalrm)(); if (weaken(_check_sigchld)) weaken(_check_sigchld)(); if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd); - return weaken(__sig_check) && weaken(__sig_check)(restartable); + res = weaken(__sig_check) && weaken(__sig_check)(restartable); + _spunlock(&__fds_lock); + return res; } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 1955a5d18..706edddf3 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -87,14 +88,17 @@ textwindows ssize_t sys_open_nt(int dirfd, const char *file, uint32_t flags, int32_t mode) { int fd; ssize_t rc; - if ((fd = __reservefd(-1)) == -1) return -1; - if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) { - rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd); - } else { - rc = sys_open_nt_file(dirfd, file, flags, mode, fd); - } - if (rc == -1) { - __releasefd(fd); + _spinlock(&__fds_lock); + if ((rc = fd = __reservefd_unlocked(-1)) != -1) { + if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) { + rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd); + } else { + rc = sys_open_nt_file(dirfd, file, flags, mode, fd); + } + if (rc == -1) { + __releasefd_unlocked(fd); + } + _spunlock(&__fds_lock); } return rc; } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 0c0db0823..fde3fa4a8 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -33,9 +34,14 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { int reader, writer; char16_t pipename[64]; CreatePipeName(pipename); - if ((reader = __reservefd(-1)) == -1) return -1; - if ((writer = __reservefd(-1)) == -1) { - __releasefd(reader); + _spinlock(&__fds_lock); + if ((reader = __reservefd_unlocked(-1)) == -1) { + _spunlock(&__fds_lock); + return -1; + } + if ((writer = __reservefd_unlocked(-1)) == -1) { + __releasefd_unlocked(reader); + _spunlock(&__fds_lock); return -1; } if (~flags & O_DIRECT) { @@ -58,12 +64,14 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { g_fds.p[writer].handle = hout; pipefd[0] = reader; pipefd[1] = writer; + _spunlock(&__fds_lock); return 0; } else { CloseHandle(hin); } } - __releasefd(writer); - __releasefd(reader); + __releasefd_unlocked(writer); + __releasefd_unlocked(reader); + _spunlock(&__fds_lock); return -1; } diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index b8664c48a..a9536e4c1 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -44,7 +44,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" -_Alignas(64) static char poll_lock; +_Alignas(64) static int poll_lock; /** * Polls on the New Technology. diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index a68a56e7c..fd266d361 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -32,10 +32,9 @@ /** * Grows file descriptor array memory if needed. */ -int __ensurefds(int fd) { +int __ensurefds_unlocked(int fd) { size_t n1, n2; struct Fd *p1, *p2; - _spinlock(&__fds_lock); n1 = g_fds.n; if (fd >= n1) { STRACE("__ensurefds(%d) extending", fd); @@ -48,7 +47,7 @@ int __ensurefds(int fd) { g_fds.p = p2; g_fds.n = n2; if (p1 != g_fds.__init_p) { - weaken(free)(p1); + __cxa_atexit(free, p1, 0); } } else { fd = enomem(); @@ -57,32 +56,44 @@ int __ensurefds(int fd) { fd = emfile(); } } + return fd; +} + +/** + * Grows file descriptor array memory if needed. + */ +int __ensurefds(int fd) { + _spinlock(&__fds_lock); + fd = __ensurefds_unlocked(fd); _spunlock(&__fds_lock); return fd; } +/** + * Finds open file descriptor slot. + */ +int __reservefd_unlocked(int start) { + int fd; + for (fd = g_fds.f; fd < g_fds.n; ++fd) { + if (!g_fds.p[fd].kind) { + break; + } + } + fd = __ensurefds_unlocked(fd); + bzero(g_fds.p + fd, sizeof(*g_fds.p)); + g_fds.p[fd].kind = kFdReserved; + return fd; +} + /** * Finds open file descriptor slot. */ int __reservefd(int start) { int fd; - for (;;) { - _spinlock(&__fds_lock); - fd = start < 0 ? g_fds.f : start; - while (fd < g_fds.n && g_fds.p[fd].kind) ++fd; - if (fd < g_fds.n) { - g_fds.f = fd + 1; - bzero(g_fds.p + fd, sizeof(*g_fds.p)); - g_fds.p[fd].kind = kFdReserved; - _spunlock(&__fds_lock); - return fd; - } else { - _spunlock(&__fds_lock); - if (__ensurefds(fd) == -1) { - return -1; - } - } - } + _spinlock(&__fds_lock); + fd = __reservefd_unlocked(start); + _spunlock(&__fds_lock); + return fd; } /** @@ -91,9 +102,12 @@ int __reservefd(int start) { static void FreeFds(void) { int i; NTTRACE("FreeFds()"); + _spinlock(&__fds_lock); for (i = 3; i < g_fds.n; ++i) { if (g_fds.p[i].kind) { + _spunlock(&__fds_lock); close(i); + _spinlock(&__fds_lock); } } if (g_fds.p != g_fds.__init_p) { @@ -102,6 +116,7 @@ static void FreeFds(void) { g_fds.p = g_fds.__init_p; g_fds.n = ARRAYLEN(g_fds.__init_p); } + _spunlock(&__fds_lock); } static textstartup void FreeFdsInit(void) { diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index eb36e030d..73f8fd4d3 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -223,7 +223,6 @@ static int __sigaction(int sig, const struct sigaction *act, rc = 0; } if (rc != -1 && !__vforked) { - _spinlock(&__sig_lock); if (oldact) { oldrva = __sighandrvas[sig]; oldact->sa_sigaction = (sigaction_f)( @@ -233,7 +232,6 @@ static int __sigaction(int sig, const struct sigaction *act, __sighandrvas[sig] = rva; __sighandflags[sig] = act->sa_flags; } - _spunlock(&__sig_lock); } return rc; } @@ -447,7 +445,9 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { if (sig == SIGKILL || sig == SIGSTOP) { rc = einval(); } else { + _spinlock(&__sig_lock); rc = __sigaction(sig, act, oldact); + _spunlock(&__sig_lock); } STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(buf[0], sizeof(buf[0]), 0, act), diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c index 6d8b7a073..8ca410c0d 100644 --- a/libc/calls/sigchld-nt.c +++ b/libc/calls/sigchld-nt.c @@ -53,7 +53,7 @@ void _check_sigchld(void) { if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) { STRACE("SIGCHILD SA_NOCLDWAIT fd=%d handle=%ld", pids[i], handles[i]); CloseHandle(handles[i]); - __releasefd(pids[i]); + __releasefd_unlocked(pids[i]); } g_fds.p[pids[i]].zombie = true; __sig_add(SIGCHLD, CLD_EXITED); diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index 14e00a01b..0d4fc2f5e 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -25,6 +25,7 @@ #include "libc/calls/struct/rusage.h" #include "libc/fmt/conv.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/nt/accounting.h" #include "libc/nt/enum/accessmask.h" @@ -57,6 +58,7 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus, struct NtProcessMemoryCountersEx memcount; struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime; if (_check_interrupts(true, g_fds.p)) return eintr(); + _spinlock(&__fds_lock); if (pid != -1 && pid != 0) { if (pid < 0) { /* XXX: this is sloppy */ @@ -67,15 +69,17 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus, if (!__isfdopen(pid) && (handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation, true, pid))) { - if ((pid = __reservefd(-1)) != -1) { + 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 { + _spunlock(&__fds_lock); CloseHandle(handle); return echild(); } } else { + _spunlock(&__fds_lock); return echild(); } } @@ -84,8 +88,12 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus, count = 1; } else { count = __sample_pids(pids, handles, false); - if (!count) return echild(); + if (!count) { + _spunlock(&__fds_lock); + return echild(); + } } + _spunlock(&__fds_lock); for (;;) { if (_check_interrupts(true, 0)) return eintr(); dwExitCode = kNtStillActive; diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index c6145da37..d88aaa3db 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -27,9 +27,11 @@ #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/asancodes.h" +#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" @@ -154,7 +156,8 @@ struct ReportOriginHeap { int z; }; -bool __asan_noreentry; +static int __asan_noreentry; +_Alignas(64) static int __asan_lock; static struct AsanMorgue __asan_morgue; #define __asan_unreachable() \ @@ -835,30 +838,27 @@ dontdiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size, } void *__asan_morgue_add(void *p) { + int i; void *r; - int i, j; - for (;;) { - i = __asan_morgue.i; - j = (i + 1) & (ARRAYLEN(__asan_morgue.p) - 1); - if (_lockcmpxchg(&__asan_morgue.i, i, j)) { - r = __asan_morgue.p[i]; - __asan_morgue.p[i] = p; - return r; - } - } + _spinlock_optimistic(&__asan_lock); + i = __asan_morgue.i++ & (ARRAYLEN(__asan_morgue.p) - 1); + r = __asan_morgue.p[i]; + __asan_morgue.p[i] = p; + _spunlock(&__asan_lock); + return r; } static void __asan_morgue_flush(void) { int i; void *p; + _spinlock_optimistic(&__asan_lock); for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { - p = __asan_morgue.p[i]; - if (_lockcmpxchg(__asan_morgue.p + i, p, 0)) { - if (weaken(dlfree)) { - weaken(dlfree)(p); - } + if (weaken(dlfree)) { + weaken(dlfree)(__asan_morgue.p[i]); } + __asan_morgue.p[i] = 0; } + _spunlock(&__asan_lock); } static size_t __asan_user_size(size_t n) { @@ -1197,12 +1197,13 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) { struct AsanTrace tr; __asan_rawtrace(&tr, __builtin_frame_address(0)); kprintf( - "WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x\n", + "WARNING: ASAN %s %s bad %d byte %s at %x bt %x %x %x %x %x\n", + __asan_noreentry == gettid() ? "error during" : "multi-threaded crash", s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3], tr.p[4], tr.p[5]); } void __asan_report_load(uint8_t *addr, int size) { - if (_lockcmpxchg(&__asan_noreentry, false, true)) { + if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) { if (!__vforked) { __asan_report_memory_fault(addr, size, "load")(); __asan_unreachable(); @@ -1215,7 +1216,7 @@ void __asan_report_load(uint8_t *addr, int size) { } void __asan_report_store(uint8_t *addr, int size) { - if (_lockcmpxchg(&__asan_noreentry, false, true)) { + if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) { if (!__vforked) { __asan_report_memory_fault(addr, size, "store")(); __asan_unreachable(); diff --git a/libc/intrin/asan.internal.h b/libc/intrin/asan.internal.h index 6bcbb2700..d74411c85 100644 --- a/libc/intrin/asan.internal.h +++ b/libc/intrin/asan.internal.h @@ -16,8 +16,6 @@ struct AsanFault { const signed char *shadow; }; -extern bool __asan_noreentry; - void __asan_unpoison(long, long); void __asan_poison(long, long, signed char); void __asan_verify(const void *, size_t); diff --git a/libc/intrin/cxaatexit.c b/libc/intrin/cxaatexit.c index a71602958..7fb83e8a0 100644 --- a/libc/intrin/cxaatexit.c +++ b/libc/intrin/cxaatexit.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/strace.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" @@ -28,6 +29,8 @@ STATIC_YOINK("__cxa_finalize"); +static int __cxa_lock; + /** * Adds global destructor. * @@ -47,6 +50,7 @@ noasan int __cxa_atexit(void *fp, void *arg, void *pred) { unsigned i; struct CxaAtexitBlock *b, *b2; _Static_assert(ATEXIT_MAX == CHAR_BIT * sizeof(b->mask), ""); + _spinlock(&__cxa_lock); b = __cxa_blocks.p; if (!b) b = __cxa_blocks.p = &__cxa_blocks.root; if (!~b->mask) { @@ -55,6 +59,7 @@ noasan int __cxa_atexit(void *fp, void *arg, void *pred) { b2->next = b; __cxa_blocks.p = b = b2; } else { + _spunlock(&__cxa_lock); return enomem(); } } @@ -64,5 +69,6 @@ noasan int __cxa_atexit(void *fp, void *arg, void *pred) { b->p[i].fp = fp; b->p[i].arg = arg; b->p[i].pred = pred; + _spunlock(&__cxa_lock); return 0; } diff --git a/libc/intrin/exit1.greg.c b/libc/intrin/exit1.greg.c index 90e43e5f1..084379892 100644 --- a/libc/intrin/exit1.greg.c +++ b/libc/intrin/exit1.greg.c @@ -19,7 +19,6 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/setjmp.internal.h" -#include "libc/intrin/winthread.internal.h" #include "libc/nt/thread.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/nr.h" @@ -34,7 +33,7 @@ */ privileged wontreturn void _Exit1(int rc) { struct WinThread *wt; - /* STRACE("_Exit1(%d)", rc); */ + STRACE("_Exit1(%d)", rc); if (!IsWindows() && !IsMetal()) { register long r10 asm("r10") = 0; asm volatile("syscall" diff --git a/libc/intrin/g_fds.c b/libc/intrin/g_fds.c index d071e00b0..9f575dcd6 100644 --- a/libc/intrin/g_fds.c +++ b/libc/intrin/g_fds.c @@ -25,7 +25,7 @@ STATIC_YOINK("_init_g_fds"); struct Fds g_fds; -_Alignas(64) char __fds_lock; +_Alignas(64) int __fds_lock; textstartup void InitializeFileDescriptors(void) { struct Fds *fds; diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index 28c5cc548..2db48b313 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/intrin/tls.h" +#include "libc/intrin/threaded.h" #include "libc/nt/thread.h" /** diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 1915f8afb..2bcc92fe0 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -31,7 +31,7 @@ #include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.internal.h" +#include "libc/intrin/threaded.h" #include "libc/limits.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" diff --git a/libc/intrin/releasefd.c b/libc/intrin/releasefd.c index 3b9410d36..660e24fcd 100644 --- a/libc/intrin/releasefd.c +++ b/libc/intrin/releasefd.c @@ -20,11 +20,15 @@ #include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" -void __releasefd(int fd) { - _spinlock(&__fds_lock); +void __releasefd_unlocked(int fd) { if (0 <= fd && fd < g_fds.n) { g_fds.p[fd].kind = 0; g_fds.f = MIN(fd, g_fds.f); } +} + +void __releasefd(int fd) { + _spinlock(&__fds_lock); + __releasefd_unlocked(fd); _spunlock(&__fds_lock); } diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index ea18cf58d..e85051b36 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -2,16 +2,26 @@ #define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/backtrace.internal.h" #include "libc/log/log.h" +#include "libc/runtime/symbols.internal.h" + +#if IsModeDbg() && !defined(_SPINLOCK_DEBUG) +#define _SPINLOCK_DEBUG +#endif #if defined(_SPINLOCK_DEBUG) -#define _spinlock(lock) _spinlock_debug(lock) +#define _spinlock(lock) _spinlock_ndebug(lock) +#define _spinlock_ndebug(lock) _spinlock_optimistic(lock) #elif defined(TINY) -#define _spinlock(lock) _spinlock_tiny(lock) +#define _spinlock(lock) _spinlock_tiny(lock) +#define _spinlock_ndebug(lock) _spinlock_tiny(lock) #else -#define _spinlock(lock) _spinlock_optimistic(lock) +#define _spinlock(lock) _spinlock_optimistic(lock) +#define _spinlock_ndebug(lock) _spinlock_optimistic(lock) #endif #define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED) @@ -44,22 +54,26 @@ } \ } while (0) -#define _spinlock_debug(lock) \ - do { \ - typeof(*(lock)) me, owner; \ - me = gettid(); \ - if (_trylock(lock)) { \ - __atomic_load(lock, &owner, __ATOMIC_RELAXED); \ - if (owner == me) { \ - kprintf("%s:%d: warning: possible spinlock re-entry in %s()\n", \ - __FILE__, __LINE__, __FUNCTION__); \ - if (weaken(ShowBacktrace)) { \ - weaken(ShowBacktrace)(2, 0); \ - } \ - } \ - _spinlock_optimistic(lock); \ - } \ - *lock = me; \ +#define _spinlock_debug(lock) \ + do { \ + typeof(*(lock)) me, owner; \ + unsigned long warntries = 10000000; \ + me = gettid(); \ + if (!_lockcmpxchg(lock, 0, me)) { \ + __atomic_load(lock, &owner, __ATOMIC_RELAXED); \ + if (owner == me) { \ + kprintf("%s:%d: warning: possible re-entry on %s in %s()\n", __FILE__, \ + __LINE__, #lock, __FUNCTION__); \ + } \ + while (!_lockcmpxchg(lock, 0, me)) { \ + if (!--warntries) { \ + warntries = -1; \ + kprintf("%s:%d: warning: possible deadlock on %s in %s()\n", \ + __FILE__, __LINE__, #lock, __FUNCTION__); \ + } \ + __builtin_ia32_pause(); \ + } \ + } \ } while (0) #endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */ diff --git a/libc/intrin/threaded.h b/libc/intrin/threaded.h new file mode 100644 index 000000000..1839a3e06 --- /dev/null +++ b/libc/intrin/threaded.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ +#define COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern bool __threaded; +extern bool __tls_enabled; +extern unsigned __tls_index; + +void *__initialize_tls(char[hasatleast 64]); +void __install_tls(char[hasatleast 64]); +char *__get_tls(void); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ */ diff --git a/libc/intrin/threaded.internal.h b/libc/intrin/threaded.internal.h deleted file mode 100644 index 0dff79330..000000000 --- a/libc/intrin/threaded.internal.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern bool __hastls; -extern bool __threaded; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_ */ diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index d78503707..0c77758f3 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -17,76 +17,67 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/intrin/tls.h" +#include "libc/errno.h" +#include "libc/intrin/threaded.h" +#include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" +#include "libc/sysv/consts/nrlinux.h" -__msabi extern typeof(TlsFree) *const __imp_TlsFree; -__msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc; -__msabi extern typeof(TlsGetValue) *const __imp_TlsGetValue; -__msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue; +#define __NR_sysarch 0x000000a5 +#define __NR___set_tcb 0x00000149 +#define __NR__lwp_setprivate 0x0000013d +#define __NR_thread_fast_set_cthread_self 0x03000003 /** - * Assigns thread-local storage slot. - * - * This function may for instance be called at startup and the result - * can be assigned to a global static variable; from then on, all the - * threads in your application may pass that value to TlsGetValue, to - * retrieve their thread-local values. - * - * @return index on success, or -1u w/ errno - * @threadsafe + * Initializes thread information block. */ -uint32_t TlsAlloc(void) { - return __imp_TlsAlloc(); +privileged void *__initialize_tls(char tib[hasatleast 64]) { + *(intptr_t *)tib = (intptr_t)tib; + *(intptr_t *)(tib + 0x30) = (intptr_t)tib; + *(int *)(tib + 0x3c) = __errno; + return tib; } /** - * Releases thread-local storage slot. - * @threadsafe + * Installs thread information block on main process. */ -bool32 TlsFree(uint32_t dwTlsIndex) { - return __imp_TlsFree(dwTlsIndex); -} - -/** - * Sets value to thread-local storage slot. - * - * @param dwTlsIndex is something returned by TlsAlloc() - * @return true if successful, otherwise false - * @threadsafe - */ -bool32 TlsSetValue(uint32_t dwTlsIndex, void *lpTlsValue) { - assert(IsWindows()); - if (dwTlsIndex < 64) { - asm("mov\t%1,%%gs:%0" - : "=m"(*((long *)0x1480 + dwTlsIndex)) - : "r"(lpTlsValue)); - return true; +privileged void __install_tls(char tib[hasatleast 64]) { + int ax, dx; + uint64_t magic; + unsigned char *p; + if (IsWindows()) { + if (!__tls_index) { + __tls_index = TlsAlloc(); + } + asm("mov\t%1,%%gs:%0" : "=m"(*((long *)0x1480 + __tls_index)) : "r"(tib)); + } else if (IsFreebsd()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_sysarch), "D"(129), "S"(tib) + : "rcx", "r11", "memory", "cc"); + } else if (IsXnu()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_thread_fast_set_cthread_self), + "D"((intptr_t)tib - 0x30) + : "rcx", "r11", "memory", "cc"); + } else if (IsOpenbsd()) { + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR___set_tcb), "D"(tib) + : "rcx", "r11", "memory", "cc"); + } else if (IsNetbsd()) { + asm volatile("syscall" + : "=a"(ax), "=d"(dx) + : "0"(__NR__lwp_setprivate), "D"(tib) + : "rcx", "r11", "memory", "cc"); } else { - return __imp_TlsSetValue(dwTlsIndex, lpTlsValue); - } -} - -/** - * Retrieves value from thread-local storage slot. - * - * @param dwTlsIndex is something returned by TlsAlloc() - * @return true if successful, otherwise false - * @threadsafe - */ -void *TlsGetValue(uint32_t dwTlsIndex) { - void *lpTlsValue; - assert(IsWindows()); - if (dwTlsIndex < 64) { - asm("mov\t%%gs:%1,%0" - : "=r"(lpTlsValue) - : "m"(*((long *)0x1480 + dwTlsIndex))); - return lpTlsValue; - // // this could also be written as... - // asm("movq\t%%gs:0x30,%0" : "=a"(tib)); - // return (void *)tib[0x1480 / 8 + dwTlsIndex]; - } else { - return __imp_TlsGetValue(dwTlsIndex); + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_linux_arch_prctl), "D"(ARCH_SET_FS), "S"(tib) + : "rcx", "r11", "memory"); } + __tls_enabled = true; } diff --git a/libc/intrin/tls.h b/libc/intrin/tls.h deleted file mode 100644 index 8f539900d..000000000 --- a/libc/intrin/tls.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_TLS_H_ -#define COSMOPOLITAN_LIBC_INTRIN_TLS_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -uint32_t TlsAlloc(void); -bool32 TlsFree(uint32_t); -bool32 TlsSetValue(uint32_t, void *); -void *TlsGetValue(uint32_t); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_TLS_H_ */ diff --git a/libc/intrin/winthread.internal.h b/libc/intrin/winthread.internal.h deleted file mode 100644 index 0ea54dbd1..000000000 --- a/libc/intrin/winthread.internal.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ -#include "libc/intrin/tls.h" -#include "libc/runtime/runtime.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct WinThread { - uint32_t tid; - int flags; - int *ctid; - int (*func)(void *); - void *arg; -}; - -extern int __winthread; - -static inline struct WinThread *GetWinThread(void) { - return TlsGetValue(__winthread); -} - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ */ diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 42766b264..0f07c5f6e 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -41,7 +41,7 @@ #define kNontrivialSize (8 * 1000 * 1000) static struct timespec vflogf_ts; -_Alignas(64) static char vflogf_lock; +_Alignas(64) static int vflogf_lock; /** * Takes corrective action if logging is on the fritz. diff --git a/libc/nexgen32e/hastls.c b/libc/nexgen32e/hastls.c deleted file mode 100644 index 84fad017e..000000000 --- a/libc/nexgen32e/hastls.c +++ /dev/null @@ -1,21 +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/intrin/threaded.internal.h" - -bool __hastls; diff --git a/libc/nexgen32e/nexgen32e.mk b/libc/nexgen32e/nexgen32e.mk index 5c6df13c2..a887887ce 100644 --- a/libc/nexgen32e/nexgen32e.mk +++ b/libc/nexgen32e/nexgen32e.mk @@ -42,7 +42,6 @@ $(LIBC_NEXGEN32E_A).pkg: \ $(LIBC_NEXGEN32E_A_OBJS) \ $(foreach x,$(LIBC_NEXGEN32E_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/nexgen32e/hastls.o \ o/$(MODE)/libc/nexgen32e/threaded.o: \ OVERRIDE_CFLAGS += \ $(NO_MAGIC) \ diff --git a/libc/nexgen32e/threaded.c b/libc/nexgen32e/threaded.c index c8589830b..a9b37305f 100644 --- a/libc/nexgen32e/threaded.c +++ b/libc/nexgen32e/threaded.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/threaded.internal.h" +#include "libc/intrin/threaded.h" bool __threaded; +bool __tls_enabled; +unsigned __tls_index; diff --git a/libc/nt/kernel32/TlsAlloc.s b/libc/nt/kernel32/TlsAlloc.s index e39628b00..9a8311965 100644 --- a/libc/nt/kernel32/TlsAlloc.s +++ b/libc/nt/kernel32/TlsAlloc.s @@ -2,7 +2,7 @@ .imp kernel32,__imp_TlsAlloc,TlsAlloc,0 .text.windows -__TlsAlloc: +TlsAlloc: push %rbp mov %rsp,%rbp .profilable @@ -10,5 +10,5 @@ __TlsAlloc: call *__imp_TlsAlloc(%rip) leave ret - .endfn __TlsAlloc,globl + .endfn TlsAlloc,globl .previous diff --git a/libc/nt/kernel32/TlsFree.s b/libc/nt/kernel32/TlsFree.s index 8a66707ad..580308a34 100644 --- a/libc/nt/kernel32/TlsFree.s +++ b/libc/nt/kernel32/TlsFree.s @@ -2,7 +2,7 @@ .imp kernel32,__imp_TlsFree,TlsFree,0 .text.windows -__TlsFree: +TlsFree: push %rbp mov %rsp,%rbp .profilable @@ -11,5 +11,5 @@ __TlsFree: call *__imp_TlsFree(%rip) leave ret - .endfn __TlsFree,globl + .endfn TlsFree,globl .previous diff --git a/libc/nt/kernel32/TlsGetValue.s b/libc/nt/kernel32/TlsGetValue.s index 371ed9084..b4c5fb727 100644 --- a/libc/nt/kernel32/TlsGetValue.s +++ b/libc/nt/kernel32/TlsGetValue.s @@ -2,7 +2,7 @@ .imp kernel32,__imp_TlsGetValue,TlsGetValue,0 .text.windows -__TlsGetValue: +TlsGetValue: push %rbp mov %rsp,%rbp .profilable @@ -11,5 +11,5 @@ __TlsGetValue: call *__imp_TlsGetValue(%rip) leave ret - .endfn __TlsGetValue,globl + .endfn TlsGetValue,globl .previous diff --git a/libc/nt/kernel32/TlsSetValue.s b/libc/nt/kernel32/TlsSetValue.s index 77d63bf4b..c53d538c5 100644 --- a/libc/nt/kernel32/TlsSetValue.s +++ b/libc/nt/kernel32/TlsSetValue.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_TlsSetValue,TlsSetValue,0 .text.windows -__TlsSetValue: +TlsSetValue: push %rbp mov %rsp,%rbp .profilable mov __imp_TlsSetValue(%rip),%rax jmp __sysv2nt - .endfn __TlsSetValue,globl + .endfn TlsSetValue,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index caefb97e5..091076566 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -302,8 +302,8 @@ imp 'EnumerateLocalComputerNamesA' EnumerateLocalComputerNamesA kernel32 3 imp 'EraseTape' EraseTape kernel32 352 imp 'EscapeCommFunction' EscapeCommFunction kernel32 0 imp 'ExecuteUmsThread' ExecuteUmsThread kernel32 354 -imp 'ExitThread' ExitThread kernel32 0 1 imp 'ExitProcess' ExitProcess kernel32 0 1 # a.k.a. RtlExitUserProcess +imp 'ExitThread' ExitThread kernel32 0 1 imp 'ExitVDM' ExitVDM kernel32 357 imp 'ExpandEnvironmentStrings' ExpandEnvironmentStringsW kernel32 0 imp 'ExpandEnvironmentStringsA' ExpandEnvironmentStringsA kernel32 0 @@ -1225,6 +1225,10 @@ imp 'TermsrvSetValueKey' TermsrvSetValueKey kernel32 1441 imp 'TermsrvSyncUserIniFileExt' TermsrvSyncUserIniFileExt kernel32 1442 imp 'Thread32First' Thread32First kernel32 1443 imp 'Thread32Next' Thread32Next kernel32 1444 +imp 'TlsAlloc' TlsAlloc kernel32 0 0 +imp 'TlsFree' TlsFree kernel32 0 1 +imp 'TlsGetValue' TlsGetValue kernel32 0 1 +imp 'TlsSetValue' TlsSetValue kernel32 0 2 imp 'Toolhelp32ReadProcessMemory' Toolhelp32ReadProcessMemory kernel32 1449 imp 'TransactNamedPipe' TransactNamedPipe kernel32 0 7 imp 'TransmitCommChar' TransmitCommChar kernel32 0 @@ -1364,10 +1368,6 @@ imp '__ReOpenFile' ReOpenFile kernel32 0 4 # TODO(jart): 6.2 and highe imp '__RemoveDirectory' RemoveDirectoryW kernel32 0 1 imp '__SetCurrentDirectory' SetCurrentDirectoryW kernel32 0 1 imp '__TerminateProcess' TerminateProcess kernel32 0 2 -imp '__TlsAlloc' TlsAlloc kernel32 0 0 -imp '__TlsFree' TlsFree kernel32 0 1 -imp '__TlsGetValue' TlsGetValue kernel32 0 1 -imp '__TlsSetValue' TlsSetValue kernel32 0 2 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 imp '__WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 diff --git a/libc/nt/thread.h b/libc/nt/thread.h index d5a8c3548..72f836b2c 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -57,6 +57,11 @@ bool32 CancelSynchronousIo(int64_t hThread); bool32 CancelIo(int64_t hFile); bool32 CancelIoEx(int64_t hFile, struct NtOverlapped *opt_lpOverlapped); +uint32_t TlsAlloc(void); +bool32 TlsFree(uint32_t); +bool32 TlsSetValue(uint32_t, void *); +void *TlsGetValue(uint32_t); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/thread.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/runtime/clone.greg.c b/libc/runtime/clone.greg.c index 990f5960f..d6c6182aa 100644 --- a/libc/runtime/clone.greg.c +++ b/libc/runtime/clone.greg.c @@ -26,9 +26,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.internal.h" -#include "libc/intrin/tls.h" -#include "libc/intrin/winthread.internal.h" +#include "libc/intrin/threaded.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" @@ -46,58 +44,22 @@ STATIC_YOINK("gettid"); // for kprintf() #define __NR_clone_linux 56 #define __NR__lwp_create 309 #define __NR_getcontext_netbsd 307 -#define __NR__lwp_setprivate 317 #define __NR_bsdthread_create 0x02000168 #define __NR_thread_fast_set_cthread_self 0x03000003 -#define __NR_sysarch 0x000000a5 -#define __NR___set_tcb 0x00000149 #define PTHREAD_START_CUSTOM_XNU 0x01000000 #define LWP_DETACHED 0x00000040 #define LWP_SUSPENDED 0x00000080 -char __tls[512]; -int __errno_global; -extern int __errno_index; +static char tibdefault[64]; -privileged void __setup_tls(void) { - int ax, dx; - uint64_t magic; - unsigned char *p; - *(intptr_t *)__tls = (intptr_t)__tls; - *(intptr_t *)(__tls + 0x30) = (intptr_t)__tls; - *(int *)(__tls + 0x3c) = __errno; - if (IsWindows()) { - __errno_index = TlsAlloc(); - TlsSetValue(__errno_index, (void *)(intptr_t)__errno); - } else if (IsLinux()) { - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_linux_arch_prctl), "D"(ARCH_SET_FS), "S"(__tls) - : "rcx", "r11", "memory"); - } else if (IsFreebsd()) { - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_sysarch), "D"(129), "S"(__tls) - : "rcx", "r11", "memory", "cc"); - } else if (IsXnu()) { - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_thread_fast_set_cthread_self), - "D"((intptr_t)__tls - 0x30) - : "rcx", "r11", "memory", "cc"); - } else if (IsOpenbsd()) { - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR___set_tcb), "D"(__tls) - : "rcx", "r11", "memory", "cc"); - } else if (IsNetbsd()) { - asm volatile("syscall" - : "=a"(ax), "=d"(dx) - : "0"(__NR__lwp_setprivate), "D"(__tls) - : "rcx", "r11", "memory", "cc"); - } - __hastls = true; -} +struct WinThread { + uint32_t tid; + int flags; + int *ctid; + void *tls; + int (*func)(void *); + void *arg; +}; uint32_t WinThreadThunk(void *warg); asm(".section\t.text.windows,\"ax\",@progbits\n\t" @@ -115,20 +77,19 @@ __attribute__((__used__, __no_reorder__)) static textwindows wontreturn void WinThreadMain(struct WinThread *wt) { int rc; + if (wt->flags & CLONE_SETTLS) { + TlsSetValue(__tls_index, wt->tls); + } if (wt->flags & CLONE_CHILD_SETTID) { *wt->ctid = wt->tid; } rc = wt->func(wt->arg); - if (wt->flags & CLONE_CHILD_CLEARTID) { - *wt->ctid = 0; - } _Exit1(rc); } static textwindows int CloneWindows(int (*func)(void *), char *stk, size_t stksz, int flags, void *arg, - int *ptid, void *tls, size_t tlssz, - int *ctid) { + void *tls, size_t tlssz, int *ctid) { int64_t h; struct WinThread *wt; wt = (struct WinThread *)(((intptr_t)(stk + stksz) - @@ -138,14 +99,11 @@ static textwindows int CloneWindows(int (*func)(void *), char *stk, wt->ctid = ctid; wt->func = func; wt->arg = arg; + wt->tls = tls; if ((h = CreateThread(0, 0, WinThreadThunk, wt, 0, &wt->tid))) { CloseHandle(h); - if (flags & CLONE_PARENT_SETTID) { - *ptid = wt->tid; - } return wt->tid; } else { - __releasefd(wt->tid); return -1; } } @@ -179,14 +137,11 @@ XnuThreadMain(void *pthread, int tid, int (*func)(void *arg), void *arg, *(int *)sp[2] = tid; } rc = func(arg); - if (sp[4] & CLONE_CHILD_CLEARTID) { - *(int *)sp[2] = 0; - } _Exit1(rc); } static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { + void *arg, void *tls, size_t tlssz, int *ctid) { int rc; bool failed; intptr_t *sp; @@ -212,9 +167,6 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, _seizelock(sp); // TODO: How can we get the tid without locking? if ((rc = bsdthread_create(fn, arg, sp, 0, PTHREAD_START_CUSTOM_XNU)) != -1) { _spinlock(sp); - if (flags & CLONE_PARENT_SETTID) { - *ptid = sp[1]; - } rc = sp[1]; } return rc; @@ -236,15 +188,11 @@ FreebsdThreadMain(intptr_t *sp) { *(int *)sp[2] = sp[4]; } rc = ((int (*)(intptr_t))sp[0])(sp[1]); - if (sp[3] & CLONE_CHILD_CLEARTID) { - *(int *)sp[2] = 0; - } _Exit1(rc); } static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, - int *ctid) { + void *arg, void *tls, size_t tlssz, int *ctid) { int ax; bool failed; int64_t tid; @@ -270,15 +218,11 @@ static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, : CFLAG_CONSTRAINT(failed), "=a"(ax) : "1"(__NR_thr_new), "D"(¶ms), "S"(sizeof(params)) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); - if (!failed) { - if (flags & CLONE_PARENT_SETTID) { - *ptid = tid; - } - return tid; - } else { + if (failed) { errno = ax; - return -1; + tid = -1; } + return tid; } struct __tfork { @@ -313,15 +257,11 @@ static privileged wontreturn void OpenbsdThreadMain(intptr_t *sp) { int rc; rc = ((int (*)(intptr_t))sp[0])(sp[1]); - if (sp[3] & CLONE_CHILD_CLEARTID) { - *(int *)sp[2] = 0; - } _Exit1(rc); } static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, - int *ctid) { + void *arg, void *tls, size_t tlssz, int *ctid) { int tid; intptr_t *sp; struct __tfork params; @@ -333,11 +273,7 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, params.tf_stack = sp; params.tf_tcb = flags & CLONE_SETTLS ? tls : 0; params.tf_tid = flags & CLONE_CHILD_SETTID ? ctid : 0; - if ((tid = __tfork(¶ms, sizeof(params), sp)) > 0) { - if (flags & CLONE_PARENT_SETTID) { - *ptid = tid; - } - } else { + if ((tid = __tfork(¶ms, sizeof(params), sp)) < 0) { errno = -tid; tid = -1; } @@ -351,15 +287,11 @@ static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg), *ctid = *tid; } rc = func(arg); - if (flags & CLONE_CHILD_CLEARTID) { - *ctid = 0; - } _Exit1(rc); } static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, - int *ctid) { + void *arg, void *tls, size_t tlssz, int *ctid) { // NetBSD has its own clone() and it works, but it's technically a // second-class API, intended to help Linux folks migrate to this! // We put it on the thread's stack, to avoid locking this function @@ -414,9 +346,6 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, : "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid) : "rcx", "r11", "memory"); if (!failed) { - if (flags & CLONE_PARENT_SETTID) { - *ptid = *tid; - } return *tid; } else { errno = ax; @@ -453,49 +382,65 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, * Creates thread. * * Threads are created in a detached manner. They currently can't be - * synchronized using wait() and posix signals. Threads created by this + * synchronized using wait() or posix signals. Threads created by this * function should be synchronized using shared memory operations. * * Any memory that's required by this system call wrapper is allocated * to the top of your stack. This is normally about 64 bytes, although * on NetBSD it's currently 800. * + * Your function is called from within the stack you specify. A return + * address is pushed onto your stack, that causes returning to jump to + * _Exit1() which terminates the thread. Even though the callback says + * it supports a return code, that'll only work on Linux and Windows. + * + * The `tls` parameter is for thread-local storage. If you specify this + * then clone() will implicitly rewire libc (e.g. errno) to use TLS: + * + * static char tib[64]; + * __initialize_tls(tib); + * __install_tls(tib); + * + * If you want a main process TLS size that's larger call it manually. + * Once you've done the above and/or started creating your own threads + * you'll be able to access your `tls` thread information block, using + * + * char *p = __get_tls(); + * printf("errno is %d\n", *(int *)(p + 0x3c)); + * * This function follows the same ABI convention as the Linux userspace * libraries, with a few small changes. The varargs has been removed to * help prevent broken code, and the stack size and tls size parameters * are introduced for compatibility with FreeBSD. * + * To keep this system call lightweight, only the thread creation use + * case is polyfilled across platforms. For example, if you want fork + * that works on OpenBSD for example, don't do it with clone(SIGCHLD) + * and please just call fork(). Even if you do that on Linux, it will + * effectively work around libc features like atfork(), so that means + * other calls like getpid() may return incorrect values. + * * @param func is your callback function * @param stk points to the bottom of a caller allocated stack, which - * must be null when fork() and vfork() equivalent flags are used - * and furthermore this must be mmap()'d using MAP_STACK in order - * to work on OpenBSD - * @param stksz is the size of that stack in bytes which must be zero - * if the fork() or vfork() equivalent flags are used it's highly - * recommended that this value be GetStackSize(), or else kprintf - * and other runtime services providing memory safety can't do as - * good and quick of a job; this value must be 16-aligned plus it - * must be at minimum 4096 bytes in size - * @param flags usually has one of - * - `SIGCHLD` will delegate to fork() - * - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork() + * must be allocated via mmap() using the MAP_STACK flag, or else + * you won't get optimal performance and it won't work on OpenBSD + * @param stksz is the size of that stack in bytes, we recommend that + * that this be set to GetStackSize() or else memory safety tools + * like kprintf() can't do as good and quick of a job; this value + * must be 16-aligned plus it must be at least 4192 bytes in size + * and it's advised to have the bottom-most page, be a guard page + * @param flags should have: * - `CLONE_THREAD|CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` - * as part high bytes, and the low order byte may optionally contain - * a signal e.g. SIGCHLD, to enable parent notification on terminate - * although the signal isn't supported on non-Linux and non-NetBSD - * at the moment; 'flags' may optionally bitwise or the following: - * - `CLONE_PARENT_SETTID` is needed for `ctid` should be set - * - `CLONE_CHILD_SETTID` is needed for `ptid` should be set - * - `CLONE_SETTLS` is needed to set `%fs` segment to `tls` + * and may optionally bitwise any of the following: + * - `CLONE_CHILD_SETTID` is needed too if you use `ctid` + * - `CLONE_SETTLS` is needed too if you set `tls` * @param arg will be passed to your callback - * @param ptid lets the parent receive the child thread id; - * this parameter is ignored if `CLONE_PARENT_SETTID` is not set * @param tls may be used to set the thread local storage segment; * this parameter is ignored if `CLONE_SETTLS` is not set - * @param tlssz is the size of tls in bytes - * @param ctid lets the child receive its thread id; - * this parameter is ignored if `CLONE_CHILD_SETTID` is not set - * @return tid on success and 0 to the child, or -1 w/ errno + * @param tlssz is the size of tls in bytes which must be at least 64 + * @param ctid lets the child receive its thread id without having to + * call gettid() and is ignored if `CLONE_CHILD_SETTID` isn't set + * @return tid of child on success, or -1 w/ errno * @threadsafe */ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, @@ -503,11 +448,11 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, int rc; __threaded = true; - if (tls && !__hastls) { - __setup_tls(); + if (tls && !__tls_enabled) { + __initialize_tls(tibdefault); + __install_tls(tibdefault); } - // verify memory is kosher if (IsAsan() && ((stksz > PAGESIZE && !__asan_is_valid((char *)stk + PAGESIZE, stksz - PAGESIZE)) || @@ -518,54 +463,27 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, ((flags & CLONE_CHILD_SETTID) && !__asan_is_valid(ctid, sizeof(*ctid))))) { rc = efault(); - } - - // delegate to bona fide clone() - else if (IsLinux()) { + } else if (!IsTiny() && + (((flags & CLONE_VM) && (stksz < PAGESIZE || (stksz & 15))) || + ((flags & CLONE_SETTLS) && (tlssz < 64 || (tlssz & 7))))) { + rc = einval(); + } else if (IsLinux()) { rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); - } - - // polyfill fork() and vfork() use cases on platforms without clone() - else if ((SupportsWindows() || SupportsBsd()) && - flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) { - if (IsTiny()) { - rc = einval(); - } else if (!arg && !stksz) { - return vfork(); // don't log clone() - } else { - rc = einval(); - } - } else if ((SupportsWindows() || SupportsBsd()) && flags == SIGCHLD) { - if (IsTiny()) { - rc = eopnotsupp(); - } else if (!arg && !stksz) { - return fork(); // don't log clone() - } else { - rc = einval(); - } - } - - // we now assume we're creating a thread - // these platforms can't do signals the way linux does - else if (!IsTiny() && ((stksz < PAGESIZE || (stksz & 15)) || - (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | - CLONE_CHILD_SETTID)) != - (CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND))) { + } else if (!IsTiny() && (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | + CLONE_CHILD_SETTID)) != + (CLONE_THREAD | CLONE_VM | CLONE_FS | + CLONE_FILES | CLONE_SIGHAND)) { rc = einval(); } else if (IsXnu()) { - rc = CloneXnu(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + rc = CloneXnu(func, stk, stksz, flags, arg, tls, tlssz, ctid); } else if (IsFreebsd()) { - rc = CloneFreebsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + rc = CloneFreebsd(func, stk, stksz, flags, arg, tls, tlssz, ctid); } else if (IsNetbsd()) { - rc = CloneNetbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + rc = CloneNetbsd(func, stk, stksz, flags, arg, tls, tlssz, ctid); } else if (IsOpenbsd()) { - rc = CloneOpenbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); - } - - // These platforms can't do segment registers like linux does - else if (IsWindows()) { - rc = CloneWindows(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + rc = CloneOpenbsd(func, stk, stksz, flags, arg, tls, tlssz, ctid); + } else if (IsWindows()) { + rc = CloneWindows(func, stk, stksz, flags, arg, tls, tlssz, ctid); } else { rc = enosys(); } diff --git a/libc/runtime/getsymboltable.greg.c b/libc/runtime/getsymboltable.greg.c index 60af4749a..38e54f0d4 100644 --- a/libc/runtime/getsymboltable.greg.c +++ b/libc/runtime/getsymboltable.greg.c @@ -20,6 +20,7 @@ #include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/strace.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" @@ -29,6 +30,7 @@ #include "libc/zip.h" #include "libc/zipos/zipos.internal.h" +static char g_lock; static struct SymbolTable *g_symtab; /** @@ -118,6 +120,7 @@ static struct SymbolTable *GetSymbolTableFromElf(void) { */ struct SymbolTable *GetSymbolTable(void) { struct Zipos *z; + if (_trylock(&g_lock)) return 0; if (!g_symtab && !__isworker) { if (weaken(__zipos_get) && (z = weaken(__zipos_get)())) { if ((g_symtab = GetSymbolTableFromZip(z))) { @@ -131,6 +134,7 @@ struct SymbolTable *GetSymbolTable(void) { g_symtab = GetSymbolTableFromElf(); } } + _spunlock(&g_lock); return g_symtab; } diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index ce551b24b..97a38801d 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -46,7 +46,7 @@ struct MemoryIntervals { size_t i, n; struct MemoryInterval *p; struct MemoryInterval s[OPEN_MAX]; - _Alignas(64) char lock; + _Alignas(64) int lock; }; extern hidden struct MemoryIntervals _mmi; diff --git a/libc/sock/closesocket-nt.c b/libc/sock/closesocket-nt.c index baf2352ef..fc7232c54 100644 --- a/libc/sock/closesocket-nt.c +++ b/libc/sock/closesocket-nt.c @@ -16,6 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/backtrace.internal.h" #include "libc/mem/mem.h" #include "libc/sock/internal.h" diff --git a/libc/sock/kntwsadata.c b/libc/sock/kntwsadata.c index 133dffe3c..32950b2da 100644 --- a/libc/sock/kntwsadata.c +++ b/libc/sock/kntwsadata.c @@ -20,6 +20,7 @@ #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/runtime.h" #include "libc/nt/winsock.h" @@ -40,12 +41,6 @@ hidden struct NtWsaData kNtWsaData; static textwindows void WinSockCleanup(void) { int i, rc; NTTRACE("WinSockCleanup()"); - for (i = g_fds.n; i--;) { - if (g_fds.p[i].kind == kFdSocket) { - close(i); - } - } - // TODO(jart): Check WSACleanup() result code rc = WSACleanup(); NTTRACE("WSACleanup() → %d% lm", rc); } diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index e919f017a..913b66e14 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/iphlpapi.h" @@ -61,11 +62,13 @@ textwindows int sys_socket_nt(int family, int type, int protocol) { sockfd->family = family; sockfd->type = truetype; sockfd->protocol = protocol; + _spinlock(&__fds_lock); g_fds.p[fd].kind = kFdSocket; g_fds.p[fd].flags = oflags; g_fds.p[fd].mode = 0140666; g_fds.p[fd].handle = h; g_fds.p[fd].extra = (uintptr_t)sockfd; + _spunlock(&__fds_lock); return fd; } else { __releasefd(fd); diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index fb71e728f..44a63c7b9 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/spinlock.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -74,6 +75,8 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { return -1; } + _spinlock(&__fds_lock); + g_fds.p[reader].kind = kFdFile; g_fds.p[reader].flags = oflags; g_fds.p[reader].mode = 0140444; @@ -84,6 +87,8 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { g_fds.p[writer].mode = 0140222; g_fds.p[writer].handle = h1; + _spunlock(&__fds_lock); + sv[0] = reader; sv[1] = writer; return 0; diff --git a/libc/stdio/fflush.internal.h b/libc/stdio/fflush.internal.h index 1dd682498..39fbe5e25 100644 --- a/libc/stdio/fflush.internal.h +++ b/libc/stdio/fflush.internal.h @@ -10,7 +10,7 @@ struct StdioFlushHandles { }; struct StdioFlush { - char lock; + int lock; struct StdioFlushHandles handles; FILE *handles_initmem[8]; }; diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 6c0914db8..2eccd28a6 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -24,7 +24,7 @@ typedef struct FILE { uint32_t size; /* 0x20 */ uint32_t nofree; /* 0x24 */ int pid; /* 0x28 */ - char lock; /* 0x2c */ + int lock; /* 0x2c */ char *getln; /* 0x30 */ } FILE; diff --git a/libc/sysv/errno.greg.c b/libc/sysv/errno.greg.c index c0476db59..bfd95d6ff 100644 --- a/libc/sysv/errno.greg.c +++ b/libc/sysv/errno.greg.c @@ -19,7 +19,8 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/intrin/threaded.internal.h" +#include "libc/intrin/threaded.h" +#include "libc/nt/thread.h" /** * Global variable for last error. @@ -34,22 +35,29 @@ * @see __errno_location() stable abi */ errno_t __errno; -int __errno_index; -privileged nocallersavedregisters errno_t *(__errno_location)(void) { - char *tib; - if (!__hastls) { - return &__errno; - } else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { - asm("mov\t%%fs:0,%0" : "=a"(tib)); - return (errno_t *)(tib + 0x3c); - } else if (IsXnu()) { - asm("mov\t%%gs:0x30,%0" : "=a"(tib)); - return (errno_t *)(tib + 0x3c); - } else if (IsWindows()) { - asm("mov\t%%gs:0x30,%0" : "=a"(tib)); - return (errno_t *)(tib + 0x1480 + __errno_index * 8); +/** + * Returns address of thread information block. + * @see __install_tls() + * @see clone() + */ +privileged nocallersavedregisters char *__get_tls(void) { + char *tib, *linear = (char *)0x30; + if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { + asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(linear)); } else { - return &__errno; + asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(linear)); + if (IsWindows()) tib = *(char **)(tib + 0x1480 + __tls_index * 8); } + return tib; +} + +/** + * Returns address of errno variable. + * @see __initialize_tls() + * @see __install_tls() + */ +privileged nocallersavedregisters errno_t *(__errno_location)(void) { + if (!__tls_enabled) return &__errno; + return (errno_t *)(__get_tls() + 0x3c); } diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 2893e06cf..caa89a114 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -153,6 +153,7 @@ noasan int main(int argc, char *argv[]) { __log_level = kLogInfo; GetOpts(argc, argv); setenv("GDB", "", true); + GetSymbolTable(); // normalize this process FixIrregularFds(); diff --git a/libc/time/localtime.c b/libc/time/localtime.c index dcede6842..db409027a 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -42,7 +42,7 @@ STATIC_YOINK("usr/share/zoneinfo/UTC"); ** POSIX-style TZ environment variable handling from Guy Harris. */ -_Alignas(64) static char locallock; +_Alignas(64) static int locallock; static int lock(void) { _spinlock(&locallock); diff --git a/libc/intrin/winthread.c b/test/libc/intrin/tls_test.c similarity index 81% rename from libc/intrin/winthread.c rename to test/libc/intrin/tls_test.c index 89c193fbe..afefb2246 100644 --- a/libc/intrin/winthread.c +++ b/test/libc/intrin/tls_test.c @@ -16,23 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/intrin/tls.h" -#include "libc/intrin/winthread.internal.h" +#include "libc/errno.h" +#include "libc/intrin/threaded.h" +#include "libc/runtime/runtime.h" +#include "libc/testlib/testlib.h" -/** - * @fileoverview TLS slot for clone() win32 polyfill. - */ +static char tib[64]; -int __winthread; - -static textstartup void __winthread_init(void) { - if (IsWindows()) { - __winthread = TlsAlloc(); - TlsSetValue(__winthread, 0); - } +TEST(tls, test) { + errno = 31337; + EXPECT_EQ(31337, errno); + EXPECT_EQ(&__errno, __errno_location()); + __initialize_tls(tib); + __install_tls(tib); + EXPECT_EQ(31337, errno); + EXPECT_EQ(tib, __get_tls()); + EXPECT_EQ(tib + 0x3c, (char *)__errno_location()); } - -const void *const __winthread_ctor[] initarray = { - __winthread_init, -}; diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index 4957f91ae..face313f4 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -23,6 +23,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/spinlock.h" +#include "libc/intrin/threaded.h" #include "libc/macros.internal.h" #include "libc/rand/rand.h" #include "libc/runtime/stack.h" @@ -74,6 +75,7 @@ TEST(rand64, testLcg_doesntProduceIdenticalValues) { } TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { + char *tls[THREADS]; sigset_t ss, oldss; void *stacks[THREADS]; int i, j, rc, ws, tid[THREADS]; @@ -89,12 +91,14 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { } ready = false; for (i = 0; i < THREADS; ++i) { + tls[i] = calloc(1, 64); + __initialize_tls(tls[i]); stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); tid[i] = clone(Thrasher, stacks[i], GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, - (void *)(intptr_t)i, 0, 0, 0, 0); + (void *)(intptr_t)i, 0, tls[i], 64, 0); ASSERT_NE(-1, tid[i]); } ready = true; @@ -113,4 +117,7 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { for (i = 0; i < THREADS; ++i) { EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize())); } + for (i = 0; i < THREADS; ++i) { + free(tls[i]); + } } diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index 1db449e66..c686b50d2 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -21,6 +21,7 @@ #include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" +#include "libc/intrin/threaded.h" #include "libc/mem/mem.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/clone.h" @@ -31,7 +32,7 @@ #include "libc/time/time.h" char *stack, *tls; -int x, me, thechilde; +int x, me, tid, thechilde; _Alignas(64) volatile char lock; void SetUp(void) { @@ -39,9 +40,8 @@ void SetUp(void) { lock = 0; me = gettid(); thechilde = 0; - tls = calloc(1, 512); - *(intptr_t *)tls = (intptr_t)tls; - *(intptr_t *)(tls + 0x30) = (intptr_t)tls; + tls = calloc(1, 64); + __initialize_tls(tls); *(int *)(tls + 0x3c) = 31337; ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0))); @@ -75,15 +75,12 @@ int DoNothing(void *arg) { return 0; } -void __setup_tls(void); - TEST(clone, test1) { - int tid; _spinlock(&lock); ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SETTLS, - (void *)23, 0, tls, 512, 0))); + (void *)23, 0, tls, 64, 0))); _spinlock(&lock); ASSERT_EQ(42, x); ASSERT_NE(me, tid); @@ -91,13 +88,27 @@ TEST(clone, test1) { ASSERT_EQ(0, errno); errno = 31337; ASSERT_EQ(31337, errno); - - return; - intptr_t *p; - asm("movq\t%%fs:0x30,%0" : "=a"(p)); - kprintf("%fs:0x30 = %p\n", p); - for (int i = 0; i < 64; ++i) { - kprintf("0x%.5x = %p\n", i * 8, p[i]); - } - kprintf("\n"); + errno = 0; +} + +int CloneTestSys(void *arg) { + thechilde = gettid(); + ASSERT_EQ(31337, errno); + open(0, 0); + ASSERT_EQ(EFAULT, errno); + _spunlock(&lock); + return 0; +} + +TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { + ASSERT_EQ(0, errno); + ASSERT_EQ(31337, *(int *)(tls + 0x3c)); + _spinlock(&lock); + ASSERT_NE(-1, (tid = clone(CloneTestSys, stack, GetStackSize(), + CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS, + (void *)23, 0, tls, 64, 0))); + _spinlock(&lock); + ASSERT_EQ(0, errno); + ASSERT_EQ(EFAULT, *(int *)(tls + 0x3c)); } diff --git a/third_party/chibicc/as.c b/third_party/chibicc/as.c index 42883b804..17a21cdb0 100644 --- a/third_party/chibicc/as.c +++ b/third_party/chibicc/as.c @@ -2921,6 +2921,7 @@ static void OnMaxpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5F); } static void OnMaxps(struct As *a, struct Slice s) { OpSse(a, 0x0F5F); } static void OnMaxsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5F); } static void OnMaxss(struct As *a, struct Slice s) { OpSse(a, 0xF30F5F); } +static void OnMfence(struct As *a, struct Slice s) { EmitVarword(a, 0x0faef0); } static void OnMinpd(struct As *a, struct Slice s) { OpSse(a, 0x660F5D); } static void OnMinps(struct As *a, struct Slice s) { OpSse(a, 0x0F5D); } static void OnMinsd(struct As *a, struct Slice s) { OpSse(a, 0xF20F5D); } @@ -3370,6 +3371,7 @@ static const struct Directive8 { {"maxps", OnMaxps}, // {"maxsd", OnMaxsd}, // {"maxss", OnMaxss}, // + {"mfence", OnMfence}, // {"minpd", OnMinpd}, // {"minps", OnMinps}, // {"minsd", OnMinsd}, // diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h index 75eb581af..171f1c8fa 100644 --- a/third_party/chibicc/chibicc.h +++ b/third_party/chibicc/chibicc.h @@ -297,62 +297,65 @@ struct Relocation { }; typedef enum { - ND_NULL_EXPR, // Do nothing - ND_ADD, // + - ND_SUB, // - - ND_MUL, // * - ND_DIV, // / - ND_NEG, // unary - - ND_REM, // % - ND_BINAND, // & - ND_BINOR, // | - ND_BINXOR, // ^ - ND_SHL, // << - ND_SHR, // >> - ND_EQ, // == - ND_NE, // != - ND_LT, // < - ND_LE, // <= - ND_ASSIGN, // = - ND_COND, // ?: - ND_COMMA, // , - ND_MEMBER, // . (struct member access) - ND_ADDR, // unary & - ND_DEREF, // unary * - ND_NOT, // ! - ND_BITNOT, // ~ - ND_LOGAND, // && - ND_LOGOR, // || - ND_RETURN, // "return" - ND_IF, // "if" - ND_FOR, // "for" or "while" - ND_DO, // "do" - ND_SWITCH, // "switch" - ND_CASE, // "case" - ND_BLOCK, // { ... } - ND_GOTO, // "goto" - ND_GOTO_EXPR, // "goto" labels-as-values - ND_LABEL, // Labeled statement - ND_LABEL_VAL, // [GNU] Labels-as-values - ND_FUNCALL, // Function call - ND_EXPR_STMT, // Expression statement - ND_STMT_EXPR, // Statement expression - ND_VAR, // Variable - ND_VLA_PTR, // VLA designator - ND_NUM, // Integer - ND_CAST, // Type cast - ND_MEMZERO, // Zero-clear a stack variable - ND_ASM, // "asm" - ND_CAS, // Atomic compare-and-swap - ND_EXCH, // Atomic exchange - ND_LOAD, // Atomic load - ND_TESTANDSET, // Atomic lock test and set - ND_RELEASE, // Atomic lock release - ND_FETCHADD, // Atomic fetch and add - ND_SUBFETCH, // Atomic sub and fetch - ND_FPCLASSIFY, // floating point classify - ND_MOVNTDQ, // Intel MOVNTDQ - ND_PMOVMSKB, // Intel PMOVMSKB + ND_NULL_EXPR, // Do nothing + ND_ADD, // + + ND_SUB, // - + ND_MUL, // * + ND_DIV, // / + ND_NEG, // unary - + ND_REM, // % + ND_BINAND, // & + ND_BINOR, // | + ND_BINXOR, // ^ + ND_SHL, // << + ND_SHR, // >> + ND_EQ, // == + ND_NE, // != + ND_LT, // < + ND_LE, // <= + ND_ASSIGN, // = + ND_COND, // ?: + ND_COMMA, // , + ND_MEMBER, // . (struct member access) + ND_ADDR, // unary & + ND_DEREF, // unary * + ND_NOT, // ! + ND_BITNOT, // ~ + ND_LOGAND, // && + ND_LOGOR, // || + ND_RETURN, // "return" + ND_IF, // "if" + ND_FOR, // "for" or "while" + ND_DO, // "do" + ND_SWITCH, // "switch" + ND_CASE, // "case" + ND_BLOCK, // { ... } + ND_GOTO, // "goto" + ND_GOTO_EXPR, // "goto" labels-as-values + ND_LABEL, // Labeled statement + ND_LABEL_VAL, // [GNU] Labels-as-values + ND_FUNCALL, // Function call + ND_EXPR_STMT, // Expression statement + ND_STMT_EXPR, // Statement expression + ND_VAR, // Variable + ND_VLA_PTR, // VLA designator + ND_NUM, // Integer + ND_CAST, // Type cast + ND_MEMZERO, // Zero-clear a stack variable + ND_ASM, // "asm" + ND_CAS, // Atomic compare-and-swap + ND_EXCH, // Atomic exchange + ND_LOAD, // Atomic load + ND_STORE, // Atomic store + ND_TESTANDSET, // Sync lock test and set + ND_TESTANDSETA, // Atomic lock test and set + ND_CLEAR, // Atomic clear + ND_RELEASE, // Atomic lock release + ND_FETCHADD, // Atomic fetch and add + ND_SUBFETCH, // Atomic sub and fetch + ND_FPCLASSIFY, // floating point classify + ND_MOVNTDQ, // Intel MOVNTDQ + ND_PMOVMSKB, // Intel PMOVMSKB } NodeKind; struct Node { @@ -394,6 +397,7 @@ struct Node { // Assembly Asm *azm; // Atomic compare-and-swap + char memorder; Node *cas_addr; Node *cas_old; Node *cas_new; diff --git a/third_party/chibicc/codegen.c b/third_party/chibicc/codegen.c index 21727cf4b..da74d080e 100644 --- a/third_party/chibicc/codegen.c +++ b/third_party/chibicc/codegen.c @@ -1547,6 +1547,14 @@ void gen_expr(Node *node) { println("\txchg\t%s,(%%rdi)", reg_ax(node->ty->size)); return; } + case ND_TESTANDSETA: { + gen_expr(node->lhs); + push(); + println("\tmov\t$1,%%eax"); + pop("%rdi"); + println("\txchg\t%s,(%%rdi)", reg_ax(node->ty->size)); + return; + } case ND_LOAD: { gen_expr(node->rhs); push(); @@ -1556,6 +1564,28 @@ void gen_expr(Node *node) { println("\tmov\t%s,(%%rdi)", reg_ax(node->ty->size)); return; } + case ND_STORE: { + gen_expr(node->lhs); + push(); + gen_expr(node->rhs); + pop("%rdi"); + println("\tmov\t(%%rax),%s", reg_ax(node->ty->size)); + println("\tmov\t%s,(%%rdi)", reg_ax(node->ty->size)); + if (node->memorder) { + println("\tmfence"); + } + return; + } + case ND_CLEAR: { + gen_expr(node->lhs); + println("\tmov\t%%rax,%%rdi"); + println("\txor\t%%eax,%%eax"); + println("\tmov\t%s,(%%rdi)", reg_ax(node->ty->size)); + if (node->memorder) { + println("\tmfence"); + } + return; + } case ND_FETCHADD: { gen_expr(node->lhs); push(); diff --git a/third_party/chibicc/kw.gperf b/third_party/chibicc/kw.gperf index 0f1c7777b..220da7e6e 100644 --- a/third_party/chibicc/kw.gperf +++ b/third_party/chibicc/kw.gperf @@ -119,8 +119,11 @@ __builtin_types_compatible_p, KW___BUILTIN_TYPES_COMPATIBLE_P "->", KW_ARROW ".", KW_DOT __atomic_load, KW___ATOMIC_LOAD -__atomic_fetch_add, KW___ATOMIC_FETCH_ADD +__atomic_store, KW___ATOMIC_STORE +__atomic_clear, KW___ATOMIC_CLEAR __atomic_sub_fetch, KW___ATOMIC_SUB_FETCH +__atomic_fetch_add, KW___ATOMIC_FETCH_ADD +__atomic_test_and_set, KW___ATOMIC_TEST_AND_SET __sync_lock_test_and_set, KW___SYNC_LOCK_TEST_AND_SET __sync_lock_release, KW___SYNC_LOCK_RELEASE __builtin_ia32_movntdq, KW___BUILTIN_IA32_MOVNTDQ diff --git a/third_party/chibicc/kw.h b/third_party/chibicc/kw.h index 62d8194eb..79aa5f796 100644 --- a/third_party/chibicc/kw.h +++ b/third_party/chibicc/kw.h @@ -112,6 +112,9 @@ #define KW___BUILTIN_IA32_MOVNTDQ 128 #define KW___ATOMIC_FETCH_ADD 129 #define KW___ATOMIC_SUB_FETCH 130 +#define KW___ATOMIC_TEST_AND_SET 131 +#define KW___ATOMIC_CLEAR 132 +#define KW___ATOMIC_STORE 133 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/third_party/chibicc/kw.inc b/third_party/chibicc/kw.inc index f570e2e53..9238b0dba 100644 --- a/third_party/chibicc/kw.inc +++ b/third_party/chibicc/kw.inc @@ -37,44 +37,44 @@ #line 10 "kw.gperf" struct thatispacked KwSlot { char *name; unsigned char code; }; -#define TOTAL_KEYWORDS 116 +#define TOTAL_KEYWORDS 119 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 28 #define MIN_HASH_VALUE 1 -#define MAX_HASH_VALUE 201 -/* maximum key range = 201, duplicates = 0 */ +#define MAX_HASH_VALUE 238 +/* maximum key range = 238, duplicates = 0 */ static inline unsigned int hash (register const char *str, register size_t len) { static const unsigned char asso_values[] = { - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 100, 202, 202, 202, 202, 65, 202, - 95, 90, 85, 15, 202, 0, 75, 202, 202, 202, - 0, 202, 202, 202, 202, 202, 10, 202, 202, 202, - 202, 202, 55, 202, 202, 202, 0, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 5, 202, 0, 50, 0, - 5, 15, 0, 40, 45, 115, 60, 5, 20, 15, - 90, 85, 0, 0, 55, 10, 0, 65, 5, 0, - 0, 10, 25, 70, 35, 30, 5, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202 + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 115, 239, 239, 239, 239, 50, 239, + 110, 105, 100, 5, 239, 0, 95, 239, 239, 239, + 10, 239, 239, 239, 239, 239, 0, 239, 239, 239, + 239, 239, 45, 239, 239, 239, 0, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 5, 239, 0, 90, 5, + 55, 10, 0, 25, 75, 105, 15, 10, 20, 15, + 125, 60, 15, 10, 10, 10, 0, 70, 5, 5, + 10, 0, 45, 85, 10, 30, 15, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239 }; register unsigned int hval = len; @@ -116,19 +116,21 @@ LookupKw (register const char *str, register size_t len) {"-", KW_MINUS}, #line 116 "kw.gperf" {"--", KW_DECREMENT}, - {""}, {""}, -#line 29 "kw.gperf" - {"const", KW_CONST}, + {""}, {""}, {""}, #line 63 "kw.gperf" {"typeof", KW_TYPEOF}, #line 62 "kw.gperf" {"typedef", KW_TYPEDEF}, - {""}, {""}, {""}, -#line 114 "kw.gperf" - {"~", KW_TILDE}, -#line 68 "kw.gperf" - {"_Atomic", KW__ATOMIC}, {""}, {""}, +#line 29 "kw.gperf" + {"const", KW_CONST}, +#line 109 "kw.gperf" + {"+", KW_PLUS}, +#line 115 "kw.gperf" + {"++", KW_INCREMENT}, +#line 20 "kw.gperf" + {"for", KW_FOR}, + {""}, #line 78 "kw.gperf" {"__restrict", KW_RESTRICT}, #line 22 "kw.gperf" @@ -143,244 +145,261 @@ LookupKw (register const char *str, register size_t len) {"__VA_OPT__", KW___VA_OPT__}, #line 13 "kw.gperf" {"struct", KW_STRUCT}, -#line 60 "kw.gperf" - {"strpbrk", KW_STRPBRK}, - {""}, {""}, -#line 31 "kw.gperf" - {"short", KW_SHORT}, -#line 28 "kw.gperf" - {"double", KW_DOUBLE}, -#line 79 "kw.gperf" - {"__restrict__", KW_RESTRICT}, -#line 95 "kw.gperf" - {"__builtin_popcount", KW___BUILTIN_POPCOUNT}, -#line 17 "kw.gperf" - {"void", KW_VOID}, -#line 69 "kw.gperf" - {"_Bool", KW__BOOL}, -#line 109 "kw.gperf" - {"+", KW_PLUS}, -#line 115 "kw.gperf" - {"++", KW_INCREMENT}, -#line 88 "kw.gperf" - {"__builtin_ffs", KW___BUILTIN_FFS}, +#line 118 "kw.gperf" + {"||", KW_LOGOR}, + {""}, #line 19 "kw.gperf" {"else", KW_ELSE}, -#line 25 "kw.gperf" - {"while", KW_WHILE}, -#line 85 "kw.gperf" - {"__builtin_compare_and_swap", KW___BUILTIN_COMPARE_AND_SWAP}, -#line 82 "kw.gperf" - {"__builtin_add_overflow", KW___BUILTIN_ADD_OVERFLOW}, -#line 81 "kw.gperf" - {"__typeof", KW_TYPEOF}, +#line 31 "kw.gperf" + {"short", KW_SHORT}, +#line 61 "kw.gperf" + {"strstr", KW_STRSTR}, +#line 79 "kw.gperf" + {"__restrict__", KW_RESTRICT}, +#line 67 "kw.gperf" + {"_Alignof", KW__ALIGNOF}, +#line 18 "kw.gperf" + {"char", KW_CHAR}, +#line 48 "kw.gperf" + {"endif", KW_ENDIF}, +#line 114 "kw.gperf" + {"~", KW_TILDE}, +#line 68 "kw.gperf" + {"_Atomic", KW__ATOMIC}, +#line 88 "kw.gperf" + {"__builtin_ffs", KW___BUILTIN_FFS}, #line 55 "kw.gperf" {"line", KW_LINE}, -#line 86 "kw.gperf" - {"__builtin_constant_p", KW___BUILTIN_CONSTANT_P}, +#line 25 "kw.gperf" + {"while", KW_WHILE}, + {""}, +#line 60 "kw.gperf" + {"strpbrk", KW_STRPBRK}, +#line 66 "kw.gperf" + {"_Alignas", KW__ALIGNAS}, +#line 47 "kw.gperf" + {"elif", KW_ELIF}, +#line 49 "kw.gperf" + {"error", KW_ERROR}, #line 74 "kw.gperf" {"__alignof__", KW___ALIGNOF__}, -#line 101 "kw.gperf" - {"__builtin_strpbrk", KW___BUILTIN_STRPBRK}, - {""}, {""}, {""}, {""}, -#line 103 "kw.gperf" - {"__builtin_sub_overflow", KW___BUILTIN_SUB_OVERFLOW}, -#line 72 "kw.gperf" - {"_Thread_local", KW__THREAD_LOCAL}, -#line 96 "kw.gperf" - {"__builtin_popcountl", KW___BUILTIN_POPCOUNTL}, -#line 97 "kw.gperf" - {"__builtin_popcountll", KW___BUILTIN_POPCOUNTLL}, -#line 56 "kw.gperf" - {"pragma", KW_PRAGMA}, -#line 92 "kw.gperf" - {"__builtin_mul_overflow", KW___BUILTIN_MUL_OVERFLOW}, -#line 104 "kw.gperf" - {"__builtin_types_compatible_p", KW___BUILTIN_TYPES_COMPATIBLE_P}, +#line 82 "kw.gperf" + {"__builtin_add_overflow", KW___BUILTIN_ADD_OVERFLOW}, +#line 44 "kw.gperf" + {"register", KW_REGISTER}, {""}, -#line 30 "kw.gperf" - {"float", KW_FLOAT}, +#line 69 "kw.gperf" + {"_Bool", KW__BOOL}, #line 87 "kw.gperf" {"__builtin_expect", KW___BUILTIN_EXPECT}, #line 119 "kw.gperf" {"->", KW_ARROW}, -#line 20 "kw.gperf" - {"for", KW_FOR}, -#line 47 "kw.gperf" - {"elif", KW_ELIF}, +#line 95 "kw.gperf" + {"__builtin_popcount", KW___BUILTIN_POPCOUNT}, + {""}, #line 91 "kw.gperf" {"__builtin_fpclassify", KW___BUILTIN_FPCLASSIFY}, -#line 108 "kw.gperf" - {"}", KW_RB}, -#line 42 "kw.gperf" - {"default", KW_DEFAULT}, - {""}, -#line 89 "kw.gperf" - {"__builtin_ffsl", KW___BUILTIN_FFSL}, -#line 90 "kw.gperf" - {"__builtin_ffsll", KW___BUILTIN_FFSLL}, - {""}, {""}, {""}, -#line 18 "kw.gperf" - {"char", KW_CHAR}, -#line 64 "kw.gperf" - {"undef", KW_UNDEF}, -#line 61 "kw.gperf" - {"strstr", KW_STRSTR}, -#line 118 "kw.gperf" - {"||", KW_LOGOR}, -#line 67 "kw.gperf" - {"_Alignof", KW__ALIGNOF}, -#line 124 "kw.gperf" - {"__sync_lock_test_and_set", KW___SYNC_LOCK_TEST_AND_SET}, -#line 49 "kw.gperf" - {"error", KW_ERROR}, -#line 58 "kw.gperf" - {"strchr", KW_STRCHR}, -#line 40 "kw.gperf" - {"defined", KW_DEFINED}, -#line 65 "kw.gperf" - {"volatile", KW_VOLATILE}, -#line 71 "kw.gperf" - {"_Static_assert", KW__STATIC_ASSERT}, -#line 48 "kw.gperf" - {"endif", KW_ENDIF}, -#line 16 "kw.gperf" - {"static", KW_STATIC}, - {""}, -#line 66 "kw.gperf" - {"_Alignas", KW__ALIGNAS}, -#line 125 "kw.gperf" - {"__sync_lock_release", KW___SYNC_LOCK_RELEASE}, - {""}, -#line 39 "kw.gperf" - {"define", KW_DEFINE}, - {""}, -#line 35 "kw.gperf" - {"continue", KW_CONTINUE}, -#line 43 "kw.gperf" - {"auto", KW_AUTO}, - {""}, #line 99 "kw.gperf" {"__builtin_strchr", KW___BUILTIN_STRCHR}, -#line 21 "kw.gperf" - {"do", KW_DO}, - {""}, {""}, {""}, {""}, {""}, -#line 70 "kw.gperf" - {"_Generic", KW__GENERIC}, +#line 103 "kw.gperf" + {"__builtin_sub_overflow", KW___BUILTIN_SUB_OVERFLOW}, +#line 72 "kw.gperf" + {"_Thread_local", KW__THREAD_LOCAL}, #line 98 "kw.gperf" {"__builtin_reg_class", KW___BUILTIN_REG_CLASS}, {""}, #line 102 "kw.gperf" {"__builtin_strstr", KW___BUILTIN_STRSTR}, -#line 84 "kw.gperf" - {"__atomic_exchange", KW___ATOMIC_EXCHANGE}, -#line 45 "kw.gperf" - {"__attribute__", KW___ATTRIBUTE__}, -#line 83 "kw.gperf" - {"__builtin_assume_aligned", KW___BUILTIN_ASSUME_ALIGNED}, +#line 92 "kw.gperf" + {"__builtin_mul_overflow", KW___BUILTIN_MUL_OVERFLOW}, +#line 81 "kw.gperf" + {"__typeof", KW_TYPEOF}, {""}, -#line 32 "kw.gperf" - {"signed", KW_SIGNED}, +#line 86 "kw.gperf" + {"__builtin_constant_p", KW___BUILTIN_CONSTANT_P}, +#line 108 "kw.gperf" + {"}", KW_RB}, +#line 101 "kw.gperf" + {"__builtin_strpbrk", KW___BUILTIN_STRPBRK}, +#line 104 "kw.gperf" + {"__builtin_types_compatible_p", KW___BUILTIN_TYPES_COMPATIBLE_P}, +#line 89 "kw.gperf" + {"__builtin_ffsl", KW___BUILTIN_FFSL}, +#line 90 "kw.gperf" + {"__builtin_ffsll", KW___BUILTIN_FFSLL}, + {""}, {""}, {""}, +#line 96 "kw.gperf" + {"__builtin_popcountl", KW___BUILTIN_POPCOUNTL}, +#line 97 "kw.gperf" + {"__builtin_popcountll", KW___BUILTIN_POPCOUNTLL}, +#line 85 "kw.gperf" + {"__builtin_compare_and_swap", KW___BUILTIN_COMPARE_AND_SWAP}, {""}, #line 77 "kw.gperf" {"__int128", KW___INT128}, -#line 24 "kw.gperf" - {"long", KW_LONG}, -#line 33 "kw.gperf" - {"break", KW_BREAK}, -#line 50 "kw.gperf" - {"extern", KW_EXTERN}, +#line 17 "kw.gperf" + {"void", KW_VOID}, +#line 64 "kw.gperf" + {"undef", KW_UNDEF}, +#line 28 "kw.gperf" + {"double", KW_DOUBLE}, + {""}, +#line 70 "kw.gperf" + {"_Generic", KW__GENERIC}, +#line 43 "kw.gperf" + {"auto", KW_AUTO}, + {""}, +#line 58 "kw.gperf" + {"strchr", KW_STRCHR}, {""}, #line 76 "kw.gperf" {"__inline", KW_INLINE}, -#line 46 "kw.gperf" - {"_Noreturn", KW__NORETURN}, {""}, {""}, -#line 12 "kw.gperf" - {"if", KW_IF}, -#line 54 "kw.gperf" - {"int", KW_INT}, +#line 39 "kw.gperf" + {"define", KW_DEFINE}, {""}, -#line 37 "kw.gperf" - {"ifdef", KW_IFDEF}, -#line 59 "kw.gperf" - {"strlen", KW_STRLEN}, +#line 57 "kw.gperf" + {"restrict", KW_RESTRICT}, + {""}, {""}, +#line 16 "kw.gperf" + {"static", KW_STATIC}, + {""}, +#line 35 "kw.gperf" + {"continue", KW_CONTINUE}, +#line 127 "kw.gperf" + {"__sync_lock_test_and_set", KW___SYNC_LOCK_TEST_AND_SET}, +#line 30 "kw.gperf" + {"float", KW_FLOAT}, +#line 56 "kw.gperf" + {"pragma", KW_PRAGMA}, {""}, #line 94 "kw.gperf" {"__builtin_offsetof", KW___BUILTIN_OFFSETOF}, -#line 34 "kw.gperf" - {"enum", KW_ENUM}, - {""}, -#line 27 "kw.gperf" - {"switch", KW_SWITCH}, -#line 93 "kw.gperf" - {"__builtin_neg_overflow", KW___BUILTIN_NEG_OVERFLOW}, -#line 57 "kw.gperf" - {"restrict", KW_RESTRICT}, -#line 51 "kw.gperf" - {"goto", KW_GOTO}, +#line 128 "kw.gperf" + {"__sync_lock_release", KW___SYNC_LOCK_RELEASE}, {""}, #line 111 "kw.gperf" {"&", KW_AMP}, #line 117 "kw.gperf" {"&&", KW_LOGAND}, -#line 80 "kw.gperf" - {"__thread", KW__THREAD_LOCAL}, +#line 45 "kw.gperf" + {"__attribute__", KW___ATTRIBUTE__}, +#line 51 "kw.gperf" + {"goto", KW_GOTO}, {""}, {""}, +#line 12 "kw.gperf" + {"if", KW_IF}, +#line 54 "kw.gperf" + {"int", KW_INT}, +#line 122 "kw.gperf" + {"__atomic_store", KW___ATOMIC_STORE}, +#line 37 "kw.gperf" + {"ifdef", KW_IFDEF}, +#line 126 "kw.gperf" + {"__atomic_test_and_set", KW___ATOMIC_TEST_AND_SET}, +#line 84 "kw.gperf" + {"__atomic_exchange", KW___ATOMIC_EXCHANGE}, +#line 65 "kw.gperf" + {"volatile", KW_VOLATILE}, + {""}, {""}, {""}, +#line 21 "kw.gperf" + {"do", KW_DO}, + {""}, +#line 71 "kw.gperf" + {"_Static_assert", KW__STATIC_ASSERT}, + {""}, #line 38 "kw.gperf" {"ifndef", KW_IFNDEF}, - {""}, -#line 23 "kw.gperf" - {"unsigned", KW_UNSIGNED}, {""}, {""}, -#line 107 "kw.gperf" - {"{", KW_LB}, +#line 24 "kw.gperf" + {"long", KW_LONG}, + {""}, {""}, {""}, {""}, +#line 123 "kw.gperf" + {"__atomic_clear", KW___ATOMIC_CLEAR}, + {""}, +#line 32 "kw.gperf" + {"signed", KW_SIGNED}, +#line 40 "kw.gperf" + {"defined", KW_DEFINED}, + {""}, {""}, {""}, +#line 53 "kw.gperf" + {"inline", KW_INLINE}, +#line 36 "kw.gperf" + {"include", KW_INCLUDE}, + {""}, {""}, {""}, +#line 50 "kw.gperf" + {"extern", KW_EXTERN}, #line 52 "kw.gperf" {"include_next", KW_INCLUDE_NEXT}, {""}, {""}, {""}, -#line 100 "kw.gperf" - {"__builtin_strlen", KW___BUILTIN_STRLEN}, -#line 126 "kw.gperf" - {"__builtin_ia32_movntdq", KW___BUILTIN_IA32_MOVNTDQ}, - {""}, {""}, {""}, -#line 120 "kw.gperf" - {".", KW_DOT}, -#line 36 "kw.gperf" - {"include", KW_INCLUDE}, -#line 122 "kw.gperf" - {"__atomic_fetch_add", KW___ATOMIC_FETCH_ADD}, - {""}, {""}, #line 14 "kw.gperf" {"return", KW_RETURN}, - {""}, {""}, {""}, -#line 26 "kw.gperf" - {"union", KW_UNION}, -#line 127 "kw.gperf" + {""}, +#line 23 "kw.gperf" + {"unsigned", KW_UNSIGNED}, +#line 46 "kw.gperf" + {"_Noreturn", KW__NORETURN}, + {""}, +#line 130 "kw.gperf" {"__builtin_ia32_pmovmskb128", KW___BUILTIN_IA32_PMOVMSKB128}, +#line 42 "kw.gperf" + {"default", KW_DEFAULT}, + {""}, +#line 34 "kw.gperf" + {"enum", KW_ENUM}, + {""}, +#line 59 "kw.gperf" + {"strlen", KW_STRLEN}, +#line 129 "kw.gperf" + {"__builtin_ia32_movntdq", KW___BUILTIN_IA32_MOVNTDQ}, + {""}, +#line 83 "kw.gperf" + {"__builtin_assume_aligned", KW___BUILTIN_ASSUME_ALIGNED}, + {""}, +#line 27 "kw.gperf" + {"switch", KW_SWITCH}, + {""}, {""}, {""}, {""}, {""}, +#line 93 "kw.gperf" + {"__builtin_neg_overflow", KW___BUILTIN_NEG_OVERFLOW}, + {""}, {""}, {""}, +#line 107 "kw.gperf" + {"{", KW_LB}, + {""}, +#line 80 "kw.gperf" + {"__thread", KW__THREAD_LOCAL}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 100 "kw.gperf" + {"__builtin_strlen", KW___BUILTIN_STRLEN}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 120 "kw.gperf" + {".", KW_DOT}, + {""}, {""}, {""}, +#line 33 "kw.gperf" + {"break", KW_BREAK}, + {""}, {""}, {""}, {""}, {""}, #line 112 "kw.gperf" {"*", KW_STAR}, {""}, #line 121 "kw.gperf" {"__atomic_load", KW___ATOMIC_LOAD}, - {""}, {""}, {""}, {""}, -#line 44 "kw.gperf" - {"register", KW_REGISTER}, - {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 106 "kw.gperf" {")", KW_RP}, - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 26 "kw.gperf" + {"union", KW_UNION}, + {""}, {""}, {""}, {""}, {""}, #line 105 "kw.gperf" {"(", KW_LP}, - {""}, {""}, {""}, {""}, -#line 53 "kw.gperf" - {"inline", KW_INLINE}, - {""}, -#line 123 "kw.gperf" - {"__atomic_sub_fetch", KW___ATOMIC_SUB_FETCH}, - {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 113 "kw.gperf" - {"!", KW_EXCLAIM} + {"!", KW_EXCLAIM}, + {""}, +#line 125 "kw.gperf" + {"__atomic_fetch_add", KW___ATOMIC_FETCH_ADD}, + {""}, {""}, {""}, {""}, +#line 124 "kw.gperf" + {"__atomic_sub_fetch", KW___ATOMIC_SUB_FETCH} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/third_party/chibicc/parse.c b/third_party/chibicc/parse.c index 7945505e8..284613ca3 100644 --- a/third_party/chibicc/parse.c +++ b/third_party/chibicc/parse.c @@ -3259,7 +3259,42 @@ static Node *primary(Token **rest, Token *tok) { tok = skip(tok, ','); node->rhs = assign(&tok, tok); tok = skip(tok, ','); - const_expr(&tok, tok); + node->memorder = const_expr(&tok, tok); + *rest = skip(tok, ')'); + return node; + } + if (kw == KW___ATOMIC_STORE) { + Node *node = new_node(ND_STORE, tok); + tok = skip(tok->next, '('); + node->lhs = assign(&tok, tok); + add_type(node->lhs); + node->ty = node->lhs->ty->base; + tok = skip(tok, ','); + node->rhs = assign(&tok, tok); + tok = skip(tok, ','); + node->memorder = const_expr(&tok, tok); + *rest = skip(tok, ')'); + return node; + } + if (kw == KW___ATOMIC_TEST_AND_SET) { + Node *node = new_node(ND_TESTANDSETA, tok); + tok = skip(tok->next, '('); + node->lhs = assign(&tok, tok); + add_type(node->lhs); + node->ty = node->lhs->ty->base; + tok = skip(tok, ','); + node->memorder = const_expr(&tok, tok); + *rest = skip(tok, ')'); + return node; + } + if (kw == KW___ATOMIC_CLEAR) { + Node *node = new_node(ND_CLEAR, tok); + tok = skip(tok->next, '('); + node->lhs = assign(&tok, tok); + add_type(node->lhs); + node->ty = node->lhs->ty->base; + tok = skip(tok, ','); + node->memorder = const_expr(&tok, tok); *rest = skip(tok, ')'); return node; } diff --git a/third_party/chibicc/test/spinlock_test.c b/third_party/chibicc/test/spinlock_test.c index 5e50af42d..07086abb0 100644 --- a/third_party/chibicc/test/spinlock_test.c +++ b/third_party/chibicc/test/spinlock_test.c @@ -15,12 +15,66 @@ #define SPUNLOCK(lock) __sync_lock_release(lock) +//////////////////////////////////////////////////////////////////////////////// + +#define SPINLOCK2(lock) \ + do { \ + for (;;) { \ + typeof(*(lock)) x; \ + __atomic_load(lock, &x, __ATOMIC_RELAXED); \ + if (!x && !__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \ + break; \ + } else { \ + __builtin_ia32_pause(); \ + } \ + } \ + } while (0) + +#define SPUNLOCK2(lock) __sync_lock_release(lock) + +//////////////////////////////////////////////////////////////////////////////// + _Alignas(64) char lock; main() { + int x, y; + ASSERT(0, lock); SPINLOCK(&lock); ASSERT(1, lock); SPUNLOCK(&lock); ASSERT(0, lock); + + ASSERT(0, lock); + SPINLOCK2(&lock); + ASSERT(1, lock); + SPUNLOCK2(&lock); + ASSERT(0, lock); + + x = 0; + y = 7; + ASSERT(0, x); + ASSERT(7, y); + __atomic_store(&x, &y, __ATOMIC_RELAXED); + ASSERT(7, x); + ASSERT(7, y); + + x = 0; + y = 7; + ASSERT(0, x); + ASSERT(7, y); + __atomic_store(&x, &y, __ATOMIC_SEQ_CST); + ASSERT(7, x); + ASSERT(7, y); + + x = 5; + y = __atomic_test_and_set(&x, __ATOMIC_SEQ_CST); + ASSERT(1, x); + ASSERT(5, y); + + x = 5; + __atomic_clear(&x, __ATOMIC_SEQ_CST); + ASSERT(0, x); + + // } diff --git a/third_party/dlmalloc/dlmalloc.greg.c b/third_party/dlmalloc/dlmalloc.greg.c index 6bc34ac80..5d34cc0e2 100644 --- a/third_party/dlmalloc/dlmalloc.greg.c +++ b/third_party/dlmalloc/dlmalloc.greg.c @@ -20,7 +20,7 @@ #include "third_party/dlmalloc/dlmalloc.h" // clang-format off -#define FOOTERS 0 +#define FOOTERS 1 #define MSPACES 0 #define HAVE_MMAP 1 From 2743f3d01271cfe2f551e64b45edfbb2554d345c Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 07:40:00 -0700 Subject: [PATCH 10/40] Clean up the clone() code a bit --- examples/greenbean.c | 2 +- libc/intrin/exit1.greg.c | 9 +- libc/intrin/gettid.greg.c | 2 +- libc/intrin/kprintf.greg.c | 2 +- libc/intrin/threaded.h | 16 --- libc/intrin/tls.greg.c | 2 +- libc/nexgen32e/checkstackalign.S | 38 ++++++ libc/nexgen32e/nexgen32e.h | 1 + libc/nexgen32e/threaded.c | 2 +- libc/nexgen32e/threaded.h | 39 ++++++ libc/runtime/clone.greg.c | 207 ++++++++++++++++++------------- libc/sysv/errno.greg.c | 24 +--- libc/thread/join.c | 14 ++- libc/thread/wait.c | 8 +- test/libc/intrin/tls_test.c | 2 +- test/libc/rand/rand64_test.c | 2 +- test/libc/runtime/clone_test.c | 19 ++- third_party/chibicc/parse.c | 1 + third_party/chibicc/preprocess.c | 8 ++ third_party/chibicc/type.c | 4 + 20 files changed, 252 insertions(+), 150 deletions(-) delete mode 100644 libc/intrin/threaded.h create mode 100644 libc/nexgen32e/checkstackalign.S create mode 100644 libc/nexgen32e/threaded.h diff --git a/examples/greenbean.c b/examples/greenbean.c index 932cb1560..45e54f612 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -18,11 +18,11 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/threaded.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" diff --git a/libc/intrin/exit1.greg.c b/libc/intrin/exit1.greg.c index 084379892..9f340725c 100644 --- a/libc/intrin/exit1.greg.c +++ b/libc/intrin/exit1.greg.c @@ -35,12 +35,11 @@ privileged wontreturn void _Exit1(int rc) { struct WinThread *wt; STRACE("_Exit1(%d)", rc); if (!IsWindows() && !IsMetal()) { - register long r10 asm("r10") = 0; - asm volatile("syscall" + asm volatile("xor\t%%r10d,%%r10d\n\t" + "syscall" : /* no outputs */ - : "a"(__NR_exit), "D"(IsLinux() ? rc : 0), "S"(0), "d"(0), - "r"(r10) - : "rcx", "r11", "memory"); + : "a"(__NR_exit), "D"(IsLinux() ? rc : 0), "S"(0), "d"(0) + : "rcx", "r10", "r11", "memory"); } else if (IsWindows()) { ExitThread(rc); } diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index 2db48b313..4331cfbd8 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/intrin/threaded.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/thread.h" /** diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 2bcc92fe0..c07ecd2e8 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -31,11 +31,11 @@ #include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.h" #include "libc/limits.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" diff --git a/libc/intrin/threaded.h b/libc/intrin/threaded.h deleted file mode 100644 index 1839a3e06..000000000 --- a/libc/intrin/threaded.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ -#define COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern bool __threaded; -extern bool __tls_enabled; -extern unsigned __tls_index; - -void *__initialize_tls(char[hasatleast 64]); -void __install_tls(char[hasatleast 64]); -char *__get_tls(void); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_THREADED_H_ */ diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index 0c77758f3..358172228 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -20,7 +20,7 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/threaded.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" #include "libc/sysv/consts/nrlinux.h" diff --git a/libc/nexgen32e/checkstackalign.S b/libc/nexgen32e/checkstackalign.S new file mode 100644 index 000000000..e362d7854 --- /dev/null +++ b/libc/nexgen32e/checkstackalign.S @@ -0,0 +1,38 @@ +/*-*- 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" + +// Checks that stack is 16-byte aligned. +// +// This function crashes if called with a misaligned stack. +_checkstackalign: + push %rbp + mov %rsp,%rbp + +/ allocate sixteen bytes + push %rax + push %rax + +/ put a value in it + xorps %xmm0,%xmm0 + movaps %xmm0,-16(%rbp) + + leave + ret + .endfn _checkstackalign,globl diff --git a/libc/nexgen32e/nexgen32e.h b/libc/nexgen32e/nexgen32e.h index 56557bba0..e9f7dacf0 100644 --- a/libc/nexgen32e/nexgen32e.h +++ b/libc/nexgen32e/nexgen32e.h @@ -7,6 +7,7 @@ extern long kHalfCache3; void imapxlatab(void *); void insertionsort(int32_t *, size_t); +void _checkstackalign(void); int64_t div10int64(int64_t) libcesque pureconst; int64_t div100int64(int64_t) libcesque pureconst; diff --git a/libc/nexgen32e/threaded.c b/libc/nexgen32e/threaded.c index a9b37305f..7db3d662b 100644 --- a/libc/nexgen32e/threaded.c +++ b/libc/nexgen32e/threaded.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/threaded.h" +#include "libc/nexgen32e/threaded.h" bool __threaded; bool __tls_enabled; diff --git a/libc/nexgen32e/threaded.h b/libc/nexgen32e/threaded.h new file mode 100644 index 000000000..c399a1499 --- /dev/null +++ b/libc/nexgen32e/threaded.h @@ -0,0 +1,39 @@ +#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ +#define COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ +#include "libc/dce.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern bool __threaded; +extern bool __tls_enabled; +extern unsigned __tls_index; + +void *__initialize_tls(char[hasatleast 64]); +void __install_tls(char[hasatleast 64]); + +#if defined(__GNUC__) && defined(__x86_64__) +/** + * Returns address of thread information block. + * + * This function must not be called until TLS is initialized. + * + * @see __install_tls() + * @see clone() + */ +static noasan inline char *__get_tls(void) { + char *tib, *lin = (char *)0x30; + if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { + asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin)); + } else { + asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin)); + if (IsWindows()) { + tib = *(char **)(tib + 0x1480 + __tls_index * 8); + } + } + return tib; +} +#endif /* GNU x86-64 */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ */ diff --git a/libc/runtime/clone.greg.c b/libc/runtime/clone.greg.c index d6c6182aa..02af5b940 100644 --- a/libc/runtime/clone.greg.c +++ b/libc/runtime/clone.greg.c @@ -26,7 +26,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" @@ -50,17 +50,32 @@ STATIC_YOINK("gettid"); // for kprintf() #define LWP_DETACHED 0x00000040 #define LWP_SUSPENDED 0x00000080 -static char tibdefault[64]; - -struct WinThread { - uint32_t tid; +struct CloneArgs { + union { + int tid; + uint32_t utid; + int64_t tid64; + }; + int lock; int flags; int *ctid; - void *tls; + char *tls; int (*func)(void *); void *arg; + void *pad; // TODO: Why does FreeBSD clobber this? }; +struct __tfork { + void *tf_tcb; + int32_t *tf_tid; + void *tf_stack; +}; + +static char tibdefault[64]; + +//////////////////////////////////////////////////////////////////////////////// +// THE NEW TECHNOLOGY + uint32_t WinThreadThunk(void *warg); asm(".section\t.text.windows,\"ax\",@progbits\n\t" ".local\tWinThreadThunk\n" @@ -69,13 +84,14 @@ asm(".section\t.text.windows,\"ax\",@progbits\n\t" "mov\t%rcx,%rdi\n\t" "mov\t%rcx,%rsp\n\t" "and\t$-16,%rsp\n\t" - "call\tWinThreadMain\n\t" + "push\t%rax\n\t" + "jmp\tWinThreadMain\n\t" ".size\tWinThreadThunk,.-WinThreadThunk\n\t" ".previous"); __attribute__((__used__, __no_reorder__)) static textwindows wontreturn void -WinThreadMain(struct WinThread *wt) { +WinThreadMain(struct CloneArgs *wt) { int rc; if (wt->flags & CLONE_SETTLS) { TlsSetValue(__tls_index, wt->tls); @@ -91,16 +107,16 @@ static textwindows int CloneWindows(int (*func)(void *), char *stk, size_t stksz, int flags, void *arg, void *tls, size_t tlssz, int *ctid) { int64_t h; - struct WinThread *wt; - wt = (struct WinThread *)(((intptr_t)(stk + stksz) - - sizeof(struct WinThread)) & - -alignof(struct WinThread)); + struct CloneArgs *wt; + wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - + sizeof(struct CloneArgs)) & + -alignof(struct CloneArgs)); wt->flags = flags; wt->ctid = ctid; wt->func = func; wt->arg = arg; wt->tls = tls; - if ((h = CreateThread(0, 0, WinThreadThunk, wt, 0, &wt->tid))) { + if ((h = CreateThread(0, 0, WinThreadThunk, wt, 0, &wt->utid))) { CloseHandle(h); return wt->tid; } else { @@ -108,45 +124,49 @@ static textwindows int CloneWindows(int (*func)(void *), char *stk, } } +//////////////////////////////////////////////////////////////////////////////// +// XNU'S NOT UNIX + void XnuThreadThunk(void *pthread, int machport, void *(*func)(void *), void *arg, intptr_t *stack, unsigned flags); asm(".local\tXnuThreadThunk\n" "XnuThreadThunk:\n\t" "xor\t%ebp,%ebp\n\t" "mov\t%r8,%rsp\n\t" + "and\t$-16,%rsp\n\t" + "push\t%rax\n\t" "jmp\tXnuThreadMain\n\t" ".size\tXnuThreadThunk,.-XnuThreadThunk"); __attribute__((__used__, __no_reorder__)) static wontreturn void XnuThreadMain(void *pthread, int tid, int (*func)(void *arg), void *arg, - intptr_t *sp, unsigned flags) { - int rc; - sp[1] = tid; - _spunlock(sp); - if (sp[4] & CLONE_SETTLS) { + struct CloneArgs *wt, unsigned flags) { + int ax; + wt->tid = tid; + _spunlock(&wt->lock); + if (wt->flags & CLONE_SETTLS) { // XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the // Go team at Google that they Apply stands by our ability to use it // https://github.com/golang/go/issues/23617#issuecomment-376662373 asm volatile("syscall" - : "=a"(rc) - : "0"(__NR_thread_fast_set_cthread_self), "D"(sp[3] - 0x30) + : "=a"(ax) + : "0"(__NR_thread_fast_set_cthread_self), "D"(wt->tls - 0x30) : "rcx", "r11", "memory", "cc"); } - if (sp[4] & CLONE_CHILD_SETTID) { - *(int *)sp[2] = tid; + if (wt->flags & CLONE_CHILD_SETTID) { + *wt->ctid = tid; } - rc = func(arg); - _Exit1(rc); + _Exit1(func(arg)); } static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, void *arg, void *tls, size_t tlssz, int *ctid) { int rc; bool failed; - intptr_t *sp; static bool once; static int broken; + struct CloneArgs *wt; if (!once) { if (bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) == -1) { broken = errno; @@ -157,38 +177,40 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, errno = broken; return -1; } - sp = (intptr_t *)(stk + stksz); - *--sp = 0; // 5 padding - *--sp = flags; // 4 clone() flags - *--sp = (intptr_t)tls; // 3 thread local storage - *--sp = (intptr_t)ctid; // 2 child tid api - *--sp = 0; // 1 receives tid - *--sp = 0; // 0 lock - _seizelock(sp); // TODO: How can we get the tid without locking? - if ((rc = bsdthread_create(fn, arg, sp, 0, PTHREAD_START_CUSTOM_XNU)) != -1) { - _spinlock(sp); - rc = sp[1]; + wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - + sizeof(struct CloneArgs)) & + -alignof(struct CloneArgs)); + wt->flags = flags; + wt->ctid = ctid; + wt->tls = tls; + _seizelock(&wt->lock); // TODO: How can we get the tid without locking? + if ((rc = bsdthread_create(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU)) != -1) { + _spinlock(&wt->lock); + rc = wt->tid; } return rc; } -void FreebsdThreadThunk(void *sp) wontreturn; +//////////////////////////////////////////////////////////////////////////////// +// FREE BESIYATA DISHMAYA + +void FreebsdThreadThunk(void *) wontreturn; asm(".local\tFreebsdThreadThunk\n" "FreebsdThreadThunk:\n\t" "xor\t%ebp,%ebp\n\t" "mov\t%rdi,%rsp\n\t" + "and\t$-16,%rsp\n\t" + "push\t%rax\n\t" "jmp\tFreebsdThreadMain\n\t" ".size\tFreebsdThreadThunk,.-FreebsdThreadThunk"); __attribute__((__used__, __no_reorder__)) static wontreturn void -FreebsdThreadMain(intptr_t *sp) { - int rc; - if (sp[3] & CLONE_CHILD_SETTID) { - *(int *)sp[2] = sp[4]; +FreebsdThreadMain(struct CloneArgs *wt) { + if (wt->flags & CLONE_CHILD_SETTID) { + *wt->ctid = wt->tid; } - rc = ((int (*)(intptr_t))sp[0])(sp[1]); - _Exit1(rc); + _Exit1(wt->func(wt->arg)); } static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, @@ -196,22 +218,23 @@ static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, int ax; bool failed; int64_t tid; - intptr_t *sp; - sp = (intptr_t *)(stk + stksz); - *--sp = 0; // 5 [padding] - *--sp = 0; // 4 [child_tid] - *--sp = flags; // 3 - *--sp = (intptr_t)ctid; // 2 - *--sp = (intptr_t)arg; // 1 - *--sp = (intptr_t)func; // 0 + struct CloneArgs *wt; + wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - + sizeof(struct CloneArgs)) & + -alignof(struct CloneArgs)); + wt->flags = flags; + wt->ctid = ctid; + wt->tls = tls; + wt->func = func; + wt->arg = arg; struct thr_param params = { .start_func = FreebsdThreadThunk, - .arg = sp, + .arg = wt, .stack_base = stk, .stack_size = stksz, .tls_base = flags & CLONE_SETTLS ? tls : 0, .tls_size = flags & CLONE_SETTLS ? tlssz : 0, - .child_tid = sp + 4, + .child_tid = &wt->tid64, .parent_tid = &tid, }; asm volatile(CFLAG_ASM("syscall") @@ -225,13 +248,10 @@ static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, return tid; } -struct __tfork { - void *tf_tcb; - int32_t *tf_tid; - void *tf_stack; -}; +//////////////////////////////////////////////////////////////////////////////// +// OPEN BESIYATA DISHMAYA -int __tfork(struct __tfork *params, size_t psize, intptr_t *stack); +int __tfork(struct __tfork *params, size_t psize, struct CloneArgs *wt); asm(".section\t.privileged,\"ax\",@progbits\n\t" ".local\t__tfork\n" "__tfork:\n\t" @@ -248,46 +268,50 @@ asm(".section\t.privileged,\"ax\",@progbits\n\t" "xor\t%ebp,%ebp\n\t" "mov\t%r8,%rsp\n\t" "mov\t%r8,%rdi\n\t" + "and\t$-16,%rsp\n\t" + "push\t%rax\n\t" "jmp\tOpenbsdThreadMain\n\t" ".size\t__tfork,.-__tfork\n\t" ".previous"); __attribute__((__used__, __no_reorder__)) static privileged wontreturn void -OpenbsdThreadMain(intptr_t *sp) { - int rc; - rc = ((int (*)(intptr_t))sp[0])(sp[1]); - _Exit1(rc); +OpenbsdThreadMain(struct CloneArgs *wt) { + _Exit1(wt->func(wt->arg)); } static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, void *arg, void *tls, size_t tlssz, int *ctid) { int tid; - intptr_t *sp; + struct CloneArgs *wt; struct __tfork params; - sp = (intptr_t *)(stk + stksz); - *--sp = flags; // 3 - *--sp = (intptr_t)ctid; // 2 - *--sp = (intptr_t)arg; // 1 - *--sp = (intptr_t)func; // 0 - params.tf_stack = sp; + wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - + sizeof(struct CloneArgs)) & + -alignof(struct CloneArgs)); + wt->flags = flags; + wt->ctid = ctid; + wt->func = func; + wt->arg = arg; + params.tf_stack = wt; params.tf_tcb = flags & CLONE_SETTLS ? tls : 0; params.tf_tid = flags & CLONE_CHILD_SETTID ? ctid : 0; - if ((tid = __tfork(¶ms, sizeof(params), sp)) < 0) { + if ((tid = __tfork(¶ms, sizeof(params), wt)) < 0) { errno = -tid; tid = -1; } return tid; } +//////////////////////////////////////////////////////////////////////////////// +// NET BESIYATA DISHMAYA + static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg), int *tid, int *ctid, int flags) { int rc; if (flags & CLONE_CHILD_SETTID) { *ctid = *tid; } - rc = func(arg); - _Exit1(rc); + _Exit1(func(arg)); } static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, @@ -353,16 +377,21 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, } } -static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, - int *ctid) { +//////////////////////////////////////////////////////////////////////////////// +// GNU/SYSTEMD + +int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, + void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { +#ifdef __chibicc__ + return -1; // TODO +#else int ax; - register int *r8 asm("r8") = tls; - register int *r10 asm("r10") = ctid; - register void *r9 asm("r9") = func; intptr_t *stack = (intptr_t *)(stk + stksz); *--stack = (intptr_t)arg; - asm volatile("syscall\n\t" + asm volatile("mov\t%4,%%r10\n\t" // ctid + "mov\t%5,%%r8\n\t" // tls + "mov\t%6,%%r9\n\t" // func + "syscall\n\t" "test\t%0,%0\n\t" "jnz\t1f\n\t" "xor\t%%ebp,%%ebp\n\t" @@ -371,13 +400,17 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, "xchg\t%%eax,%%edi\n\t" "jmp\t_Exit1\n1:" : "=a"(ax) - : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "r"(r10), - "r"(r8), "r"(r9) - : "rcx", "r11", "memory"); + : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "g"(ctid), + "g"(tls), "g"(func) + : "rcx", "r8", "r9", "r10", "r11", "memory"); if (ax > -4096u) errno = -ax, ax = -1; return ax; +#endif } +//////////////////////////////////////////////////////////////////////////////// +// COSMOPOLITAN + /** * Creates thread. * @@ -446,9 +479,13 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { int rc; + struct CloneArgs *wt; - __threaded = true; - if (tls && !__tls_enabled) { + if (flags & CLONE_THREAD) { + __threaded = true; + } + + if ((flags & CLONE_SETTLS) && !__tls_enabled) { __initialize_tls(tibdefault); __install_tls(tibdefault); } diff --git a/libc/sysv/errno.greg.c b/libc/sysv/errno.greg.c index bfd95d6ff..babf13461 100644 --- a/libc/sysv/errno.greg.c +++ b/libc/sysv/errno.greg.c @@ -16,11 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" -#include "libc/calls/calls.h" -#include "libc/dce.h" -#include "libc/intrin/threaded.h" -#include "libc/nt/thread.h" +#include "libc/errno.h" +#include "libc/nexgen32e/threaded.h" /** * Global variable for last error. @@ -36,24 +33,9 @@ */ errno_t __errno; -/** - * Returns address of thread information block. - * @see __install_tls() - * @see clone() - */ -privileged nocallersavedregisters char *__get_tls(void) { - char *tib, *linear = (char *)0x30; - if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { - asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(linear)); - } else { - asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(linear)); - if (IsWindows()) tib = *(char **)(tib + 0x1480 + __tls_index * 8); - } - return tib; -} - /** * Returns address of errno variable. + * * @see __initialize_tls() * @see __install_tls() */ diff --git a/libc/thread/join.c b/libc/thread/join.c index e816a0204..9dfaa0ed7 100644 --- a/libc/thread/join.c +++ b/libc/thread/join.c @@ -34,13 +34,15 @@ int cthread_join(cthread_t td, int* rc) { : "cc"); if (!(state & cthread_finished)) { + int ax; int flags = FUTEX_WAIT; // PRIVATE makes it hang - register struct timespec* timeout asm("r10") = NULL; - asm volatile("syscall" - : /* no outputs */ - : "a"(__NR_futex), "D"(&td->tid), "S"(flags), "d"(tid), - "r"(timeout) - : "rcx", "r11", "cc", "memory"); + struct timespec* timeout = NULL; + asm volatile("mov\t%5,%%r10\n\t" // timeout + "syscall" + : "=a"(ax) + : "0"(__NR_futex), "D"(&td->tid), "S"(flags), "d"(tid), + "g"(timeout) + : "rcx", "r10", "r11", "cc", "memory"); } *rc = td->rc; diff --git a/libc/thread/wait.c b/libc/thread/wait.c index c0ecb7427..1d6565e14 100644 --- a/libc/thread/wait.c +++ b/libc/thread/wait.c @@ -25,12 +25,12 @@ int cthread_memory_wait32(uint32_t* addr, uint32_t val, if (__NR_futex != 0xfff) { int flags = FUTEX_WAIT; int rc; - register struct timespec* timeout_ asm("r10") = timeout; - asm volatile("syscall" + asm volatile("mov\t%5,%%r10\n\t" // timeout + "syscall" : "=a"(rc) : "0"(__NR_futex), "D"(addr), "S"(flags), "d"(val), - "r"(timeout_) - : "rcx", "r11", "cc", "memory"); + "g"(timeout) + : "rcx", "r10", "r11", "cc", "memory"); return rc; } return -1; diff --git a/test/libc/intrin/tls_test.c b/test/libc/intrin/tls_test.c index afefb2246..42d1136c4 100644 --- a/test/libc/intrin/tls_test.c +++ b/test/libc/intrin/tls_test.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/intrin/threaded.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index face313f4..90885a554 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -23,8 +23,8 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/threaded.h" #include "libc/rand/rand.h" #include "libc/runtime/stack.h" #include "libc/str/str.h" diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index c686b50d2..4f4c4c409 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -21,13 +21,15 @@ #include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" -#include "libc/intrin/threaded.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sig.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/time/time.h" @@ -48,15 +50,11 @@ void SetUp(void) { } void TearDown(void) { - if (thechilde) { - tkill(thechilde, SIGKILL); - errno = 0; - } - sched_yield(); free(tls); } int CloneTest1(void *arg) { + _checkstackalign(); x = 42; if (!IsWindows()) { ASSERT_EQ(31337, errno); @@ -72,6 +70,7 @@ int CloneTest1(void *arg) { } int DoNothing(void *arg) { + _checkstackalign(); return 0; } @@ -92,6 +91,7 @@ TEST(clone, test1) { } int CloneTestSys(void *arg) { + _checkstackalign(); thechilde = gettid(); ASSERT_EQ(31337, errno); open(0, 0); @@ -112,3 +112,10 @@ TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { ASSERT_EQ(0, errno); ASSERT_EQ(EFAULT, *(int *)(tls + 0x3c)); } + +BENCH(clone, bench) { + errno_t *volatile ep; + char *volatile tp; + EZBENCH2("__errno_location", donothing, (ep = (__errno_location()))); + EZBENCH2("__get_tls", donothing, (tp = __get_tls())); +} diff --git a/third_party/chibicc/parse.c b/third_party/chibicc/parse.c index 284613ca3..35b1f2e1e 100644 --- a/third_party/chibicc/parse.c +++ b/third_party/chibicc/parse.c @@ -559,6 +559,7 @@ static Token *thing_attributes(Token *tok, void *arg) { consume_attribute(&tok, tok, "warn_unused_result") || consume_attribute(&tok, tok, "flatten") || consume_attribute(&tok, tok, "leaf") || + consume_attribute(&tok, tok, "no_reorder") || consume_attribute(&tok, tok, "dontthrow") || consume_attribute(&tok, tok, "optnone") || consume_attribute(&tok, tok, "returns_twice") || diff --git a/third_party/chibicc/preprocess.c b/third_party/chibicc/preprocess.c index d71363dd6..a5a6acd74 100644 --- a/third_party/chibicc/preprocess.c +++ b/third_party/chibicc/preprocess.c @@ -1174,6 +1174,14 @@ __INT_FAST32_TYPE__\000\ int\000\ __UINT_FAST32_TYPE__\000\ unsigned\000\ +__INT_FAST8_MAX__\000\ +0x7f\000\ +__INT_FAST16_MAX__\000\ +0x7fffffff\000\ +__INT_FAST32_MAX__\000\ +0x7fffffff\000\ +__INT_FAST64_MAX__\000\ +0x7fffffffffffffffl\000\ __INT_FAST64_TYPE__\000\ long\000\ __UINT_FAST64_TYPE__\000\ diff --git a/third_party/chibicc/type.c b/third_party/chibicc/type.c index f3c4bba03..5ddac510d 100644 --- a/third_party/chibicc/type.c +++ b/third_party/chibicc/type.c @@ -1,3 +1,4 @@ +#include "libc/assert.h" #include "third_party/chibicc/chibicc.h" /* TODO(jart): Why can't these be const? */ @@ -144,6 +145,9 @@ static Type *get_common_type(Type *ty1, Type *ty2) { // // This operation is called the "usual arithmetic conversion". static void usual_arith_conv(Node **lhs, Node **rhs) { + if (!(*lhs)->ty || !(*rhs)->ty) { + error_tok((*lhs)->tok, "internal npe error"); + } Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty); *lhs = new_cast(*lhs, ty); *rhs = new_cast(*rhs, ty); From f37524ef4fbfd41504a38d71bd6712610724a6e8 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 07:42:43 -0700 Subject: [PATCH 11/40] Add ANSI mode guard to header --- libc/nexgen32e/threaded.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/nexgen32e/threaded.h b/libc/nexgen32e/threaded.h index c399a1499..0b6a61b0b 100644 --- a/libc/nexgen32e/threaded.h +++ b/libc/nexgen32e/threaded.h @@ -11,7 +11,7 @@ extern unsigned __tls_index; void *__initialize_tls(char[hasatleast 64]); void __install_tls(char[hasatleast 64]); -#if defined(__GNUC__) && defined(__x86_64__) +#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) /** * Returns address of thread information block. * From 3d99ebb68c87e3dd004aca7d90907189b6e040a9 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 10:41:23 -0700 Subject: [PATCH 12/40] Add raw parameter to redbean lua compress --- test/libc/calls/getcwd_test.c | 8 ++--- test/libc/calls/sigaction_test.c | 42 ++++++++++++---------- tool/net/help.txt | 32 +++++++++++++---- tool/net/lfuncs.c | 60 +++++++++++++++++++++----------- 4 files changed, 92 insertions(+), 50 deletions(-) diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index 022ca22b6..af7eb7851 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -30,14 +30,14 @@ char testlib_enable_tmp_setup_teardown; TEST(getcwd, test) { char buf[PATH_MAX]; - EXPECT_NE(-1, mkdir("subdir", 0755)); - EXPECT_NE(-1, chdir("subdir")); + EXPECT_SYS(0, 0, mkdir("subdir", 0755)); + EXPECT_SYS(0, 0, chdir("subdir")); EXPECT_STREQ("subdir", basename(getcwd(buf, ARRAYLEN(buf)))); } TEST(getcwd, testNullBuf_allocatesResult) { - EXPECT_NE(-1, mkdir("subdir", 0755)); - EXPECT_NE(-1, chdir("subdir")); + EXPECT_SYS(0, 0, mkdir("subdir", 0755)); + EXPECT_SYS(0, 0, chdir("subdir")); EXPECT_STREQ("subdir", basename(gc(getcwd(0, 0)))); } diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index fe4983c30..2cece08e6 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -23,6 +23,7 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" @@ -34,6 +35,7 @@ struct sigaction oldsa; volatile bool gotsigint; void OnSigInt(int sig) { + _checkstackalign(); gotsigint = true; } @@ -46,11 +48,11 @@ void SetUp(void) { TEST(sigaction, raise) { struct sigaction saint = {.sa_handler = OnSigInt}; - EXPECT_NE(-1, sigaction(SIGINT, &saint, &oldsa)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &saint, &oldsa)); ASSERT_FALSE(gotsigint); EXPECT_NE(-1, raise(SIGINT)); ASSERT_TRUE(gotsigint); - EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &oldsa, NULL)); } //////////////////////////////////////////////////////////////////////////////// @@ -68,37 +70,36 @@ TEST(sigaction, testPingPongParentChildWithSigint) { // kind of runner. todo(fixme!) return; } - EXPECT_NE(-1, sigemptyset(&blockint)); - EXPECT_NE(-1, sigaddset(&blockint, SIGINT)); - EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &blockint, &oldmask)); - EXPECT_NE(-1, sigaction(SIGINT, &catchint, &oldint)); + EXPECT_SYS(0, 0, sigemptyset(&blockint)); + EXPECT_SYS(0, 0, sigaddset(&blockint, SIGINT)); + EXPECT_SYS(0, 0, sigprocmask(SIG_BLOCK, &blockint, &oldmask)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, &oldint)); ASSERT_NE(-1, (pid = fork())); if (!pid) { // ping - EXPECT_NE(-1, kill(getppid(), SIGINT)); + EXPECT_SYS(0, 0, kill(getppid(), SIGINT)); EXPECT_FALSE(gotsigint); // pong - EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); - EXPECT_EQ(-1, sigsuspend(0)); - EXPECT_EQ(EINTR, errno); + EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, 0)); + EXPECT_SYS(EINTR, -1, sigsuspend(0)); EXPECT_TRUE(gotsigint); _exit(0); } // pong EXPECT_FALSE(gotsigint); - EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); - EXPECT_EQ(-1, sigsuspend(0)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, 0)); + EXPECT_SYS(EINTR, -1, sigsuspend(0)); EXPECT_TRUE(gotsigint); // ping - EXPECT_NE(-1, sigaction(SIGINT, &ignoreint, 0)); - EXPECT_NE(-1, kill(pid, SIGINT)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &ignoreint, 0)); + EXPECT_SYS(0, 0, kill(pid, SIGINT)); // cleanup EXPECT_NE(-1, wait4(pid, &status, 0, 0)); EXPECT_EQ(1, WIFEXITED(status)); EXPECT_EQ(0, WEXITSTATUS(status)); EXPECT_EQ(0, WTERMSIG(status)); - EXPECT_NE(-1, sigaction(SIGINT, &oldint, 0)); - EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &oldmask, 0)); + EXPECT_SYS(0, 0, sigaction(SIGINT, &oldint, 0)); + EXPECT_SYS(0, 0, sigprocmask(SIG_BLOCK, &oldmask, 0)); } //////////////////////////////////////////////////////////////////////////////// @@ -108,6 +109,7 @@ TEST(sigaction, testPingPongParentChildWithSigint) { volatile int trapeax; void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) { + _checkstackalign(); trapeax = ctx->uc_mcontext.rax; } @@ -116,7 +118,7 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) { EXPECT_NE(-1, sigaction(SIGTRAP, &saint, &oldsa)); asm("int3" : /* no outputs */ : "a"(0x31337)); EXPECT_EQ(0x31337, trapeax); - EXPECT_NE(-1, sigaction(SIGTRAP, &oldsa, NULL)); + EXPECT_SYS(0, 0, sigaction(SIGTRAP, &oldsa, NULL)); } //////////////////////////////////////////////////////////////////////////////// @@ -124,6 +126,7 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) { // test signal handler can modify cpu registers (now it's recoverable!) void SkipOverFaultingInstruction(struct ucontext *ctx) { + _checkstackalign(); struct XedDecodedInst xedd; xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); @@ -131,6 +134,7 @@ void SkipOverFaultingInstruction(struct ucontext *ctx) { } void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { + _checkstackalign(); SkipOverFaultingInstruction(ctx); ctx->uc_mcontext.rax = 42; ctx->uc_mcontext.rdx = 0; @@ -138,10 +142,10 @@ void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { noubsan void ubsanTrumpsSystemsEngineering(void) { struct sigaction saint = {.sa_sigaction = OnFpe, .sa_flags = SA_SIGINFO}; - EXPECT_NE(-1, sigaction(SIGFPE, &saint, &oldsa)); + EXPECT_SYS(0, 0, sigaction(SIGFPE, &saint, &oldsa)); volatile long x = 0; EXPECT_EQ(42, 666 / x); /* systems engineering trumps math */ - EXPECT_NE(-1, sigaction(SIGFPE, &oldsa, NULL)); + EXPECT_SYS(0, 0, sigaction(SIGFPE, &oldsa, NULL)); } TEST(sigaction, sigFpe_handlerCanEditProcessStateAndRecoverExecution) { diff --git a/tool/net/help.txt b/tool/net/help.txt index 2bad38c48..4cef7ae1a 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1308,7 +1308,7 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. - Compress(uncompdata:str[, level:int]) → compdata:str + Compress(uncompdata:str[, level:int[, raw:bool]]) → compdata:str Compresses data using DEFLATE algorithm. The compression format here is defined to be quick and handy for things like @@ -1319,18 +1319,27 @@ FUNCTIONS >: Uncompress(Compress('hello')) "hello" - `level` is the compression level, which defaults to 7. The max - is 10. Lower numbers go faster. Higher numbers go slower, but - have better compression ratios. - - [implementation details] The binary wire format is defined as follows: 1. uleb64 uncompressed byte size (1 to 10 bytes) 2. uint32_t crc32 (4 bytes; zlib polynomial) 3. data (created by zlib compress function) - Uncompress(compdata:str) → uncompdata:str + `level` is the compression level, which defaults to 7. The max + is 10. Lower numbers go faster. Higher numbers go slower, but + have better compression ratios. + + `raw` may be set to true if you only want `data` (3) to be + returned. In this case, it's assumed the caller will take + responsibility for storing the length (and optionall crc) + separately. See the redbean Crc32() API. + + >: a = 'hello' + >: b = Compress(a, 9, true) + >: Uncompress(b, #a) + "hello" + + Uncompress(compdata:str[, uncomplen:int]) → uncompdata:str Uncompresses data using DEFLATE algorithm. This applies the inverse transform of the Compress() function. See its docs for @@ -1341,6 +1350,15 @@ FUNCTIONS of validity iron-clad. It's implemented using Intel CLMUL so it has ludicrous speed performance as well. + If you used the `raw` parameter when calling Compress() i.e. + `compdata` doesn't have the redbean header described above, + then the `uncomplen` parameter may be supplied. IN that case + your data is handed over directly to zlib `uncompress()`. In + this case an exception will be raised if the value couldn't be + decoded, or if the resulting length differed from the supplied + length. It's recommended that Crc32() check still be performed + manually after using this method. + Benchmark(func[, count[, maxattempts]]) └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 0304038e8..74a4f989c 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -683,6 +683,7 @@ int LuaBenchmark(lua_State *L) { } int LuaCompress(lua_State *L) { + bool raw; size_t n, m; char *q, *e; uint32_t crc; @@ -691,14 +692,23 @@ int LuaCompress(lua_State *L) { p = luaL_checklstring(L, 1, &n); level = luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); m = compressBound(n); - CHECK_NOTNULL((q = LuaAlloc(L, 10 + 4 + m))); - crc = crc32_z(0, p, n); - e = uleb64(q, n); - e = WRITE32LE(e, crc); - hdrlen = e - q; - CHECK_EQ(Z_OK, compress2((unsigned char *)(q + hdrlen), &m, - (unsigned char *)p, n, level)); - lua_pushlstring(L, q, hdrlen + m); + if (lua_toboolean(L, 3)) { + // raw mode + CHECK_NOTNULL((q = LuaAlloc(L, m))); + CHECK_EQ(Z_OK, + compress2((unsigned char *)q, &m, (unsigned char *)p, n, level)); + lua_pushlstring(L, q, m); + } else { + // easy mode + CHECK_NOTNULL((q = LuaAlloc(L, 10 + 4 + m))); + crc = crc32_z(0, p, n); + e = uleb64(q, n); + e = WRITE32LE(e, crc); + hdrlen = e - q; + CHECK_EQ(Z_OK, compress2((unsigned char *)(q + hdrlen), &m, + (unsigned char *)p, n, level)); + lua_pushlstring(L, q, hdrlen + m); + } free(q); return 1; } @@ -710,18 +720,28 @@ int LuaUncompress(lua_State *L) { const char *p; size_t n, m, len; p = luaL_checklstring(L, 1, &n); - if ((rc = unuleb64(p, n, &m)) == -1 || n < rc + 4) { - luaL_error(L, "compressed value too short to be valid"); - unreachable; - } - len = m; - crc = READ32LE(p + rc); - CHECK_NOTNULL((q = LuaAlloc(L, m))); - if (uncompress((void *)q, &m, (unsigned char *)p + rc + 4, n) != Z_OK || - m != len || crc32_z(0, q, m) != crc) { - free(q); - luaL_error(L, "compressed value is corrupted"); - unreachable; + if (lua_isnoneornil(L, 2)) { + if ((rc = unuleb64(p, n, &m)) == -1 || n < rc + 4) { + luaL_error(L, "compressed value too short to be valid"); + unreachable; + } + len = m; + crc = READ32LE(p + rc); + CHECK_NOTNULL((q = LuaAlloc(L, m))); + if (uncompress((void *)q, &m, (unsigned char *)p + rc + 4, n) != Z_OK || + m != len || crc32_z(0, q, m) != crc) { + free(q); + luaL_error(L, "compressed value is corrupted"); + unreachable; + } + } else { + len = m = luaL_checkinteger(L, 2); + CHECK_NOTNULL((q = LuaAlloc(L, m))); + if (uncompress((void *)q, &m, (void *)p, n) != Z_OK || m != len) { + free(q); + luaL_error(L, "compressed value is corrupted"); + unreachable; + } } lua_pushlstring(L, q, m); free(q); From 2ad1c9078d4170cae218411b90a7b1ae2b051ff8 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 11:38:35 -0700 Subject: [PATCH 13/40] Improve dependency generation for libcxx headers This change also updates redbean to have more informative error messages based on the ones that are actually showing up in production. --- build/bootstrap/mkdeps.com | Bin 69632 -> 77824 bytes tool/build/mkdeps.c | 3 +++ tool/net/redbean.c | 45 +++++++++++++++++++++++++++++-------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/build/bootstrap/mkdeps.com b/build/bootstrap/mkdeps.com index 9b9e0cc16421c800cc67a195c99aea34dc470eaa..7a8296c2d66fc45b8123d60a4f48dee53faf88d6 100755 GIT binary patch delta 47093 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 0) { p += rc; n -= rc; + } else if (rc == MBEDTLS_ERR_NET_CONN_RESET) { + errno = ECONNRESET; + return -1; + } else if (rc == MBEDTLS_ERR_SSL_TIMEOUT) { + errno = ETIMEDOUT; + return -1; } else { WARNF("(ssl) %s SslWrite error -0x%04x", DescribeClient(), -rc); errno = EIO; @@ -5519,6 +5540,10 @@ static char *HandleMapFailed(struct Asset *a, int fd) { return ServeError(500, "Internal Server Error"); } +static void LogAcceptError(const char *s) { + WARNF("(srvr) %s accept error: %s", DescribeServer(), s); +} + static char *HandleOpenFail(struct Asset *a) { LockInc(&shared->c.openfails); WARNF("(srvr) open(%`'s) error: %m", a->file->path); @@ -6688,35 +6713,37 @@ static int HandleConnection(size_t i) { LockInc(&shared->c.acceptinterrupts); } else if (errno == ENFILE) { LockInc(&shared->c.enfiles); - WARNF("(srvr) too many open files"); + LogAcceptError("too many open files"); meltdown = true; } else if (errno == EMFILE) { LockInc(&shared->c.emfiles); - WARNF("(srvr) ran out of open file quota"); + LogAcceptError("ran out of open file quota"); meltdown = true; } else if (errno == ENOMEM) { LockInc(&shared->c.enomems); - WARNF("(srvr) ran out of memory"); + LogAcceptError("ran out of memory"); meltdown = true; } else if (errno == ENOBUFS) { LockInc(&shared->c.enobufs); - WARNF("(srvr) ran out of buffer"); + LogAcceptError("ran out of buffer"); meltdown = true; } else if (errno == ENONET) { LockInc(&shared->c.enonets); - WARNF("(srvr) %s network gone", DescribeServer()); + LogAcceptError("network gone"); polls[i].fd = -polls[i].fd; } else if (errno == ENETDOWN) { LockInc(&shared->c.enetdowns); - WARNF("(srvr) %s network down", DescribeServer()); + LogAcceptError("network down"); polls[i].fd = -polls[i].fd; } else if (errno == ECONNABORTED) { LockInc(&shared->c.acceptresets); - WARNF("(srvr) %s connection reset before accept"); + WARNF("(srvr) %S accept error: %s", DescribeServer(), + "connection reset before accept"); } else if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) { LockInc(&shared->c.accepterrors); - WARNF("(srvr) %s ephemeral accept error: %m", DescribeServer()); + WARNF("(srvr) accept error: %s ephemeral accept error: %m", + DescribeServer()); } else { DIEF("(srvr) %s accept error: %m", DescribeServer()); } @@ -6832,7 +6859,7 @@ static int HandlePoll(int ms) { LockInc(&shared->c.pollinterrupts); } else if (errno == ENOMEM) { LockInc(&shared->c.enomems); - WARNF("(srvr) %s ran out of memory"); + WARNF("(srvr) poll error: ran out of memory"); meltdown = true; } else { DIEF("(srvr) poll error: %m"); From b2c6ec6eab559426468103865aaf8835845f058b Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 12:10:22 -0700 Subject: [PATCH 14/40] Make linenoise properly recover from backgrounding --- third_party/linenoise/linenoise.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 4c0140ce2..a0df3a16e 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -772,7 +772,8 @@ static ssize_t linenoiseRead(int fd, char *buf, size_t size, return -1; } if (gotcont && rawmode != -1) { - linenoiseEnableRawMode(rawmode); + rawmode = -1; + linenoiseEnableRawMode(0); if (l) refreshme = 1; } if (l && gotwinch) refreshme = 1; From c6bbca55e9f977e386f619d08b343268f7ca6544 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 17 May 2022 12:51:25 -0700 Subject: [PATCH 15/40] Disable Linux vDSO support for now It's been encountering some strange errors that are difficult to reproduce. --- libc/calls/clock_gettime.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index f7deab6e6..0ccd93dc2 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -80,6 +80,8 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { * Returns fast system clock_gettime() if it exists. */ void *__get_clock_gettime(void) { + // TODO(jart): Re-enable this. + return 0; void *vdso; static bool once; static void *result; From 9208c83f7a39efc966bf3ce22043d2c008d40398 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 18 May 2022 16:41:29 -0700 Subject: [PATCH 16/40] Make some systemic improvements - add vdso dump utility - tests now log stack usage - rename g_ftrace to __ftrace - make internal spinlocks go faster - add conformant c11 atomics library - function tracing now logs stack usage - make function call tracing thread safe - add -X unsecure (no ssl) mode to redbean - munmap() has more consistent behavior now - pacify fsync() calls on python unit tests - make --strace flag work better in redbean - start minimizing and documenting compiler flags --- build/htags | 4 + examples/greenbean.c | 5 +- libc/atomic.h | 80 ++++ libc/bits/atomic-clang.h | 36 ++ libc/bits/atomic-gcc.h | 58 +++ libc/bits/atomic-gcc47.h | 34 ++ libc/bits/atomic.h | 74 ++-- libc/calls/calls.mk | 98 +++-- libc/calls/close-nt.c | 8 +- .../describesigaltstack.greg.c} | 41 +- libc/calls/dup-nt.c | 15 +- libc/calls/fdatasync.c | 17 +- libc/calls/fsync.c | 15 +- libc/calls/internal.h | 6 +- libc/calls/mkntcmdline.c | 6 +- libc/calls/mkntenvblock.c | 8 +- libc/calls/{sched_yield.c => nosync.c} | 23 +- libc/calls/poll-nt.c | 1 - libc/calls/printfds.c | 1 - libc/calls/readlinkat.c | 3 + libc/calls/realpath.c | 5 +- libc/calls/sigaltstack.c | 16 +- libc/calls/strace.internal.h | 3 +- libc/calls/sync.c | 13 +- libc/calls/weirdtypes.h | 61 --- libc/integral/c.inc | 6 +- libc/integral/normalize.inc | 3 + libc/intrin/asan.c | 65 ++-- libc/intrin/asan.internal.h | 4 +- libc/intrin/assertfail.greg.c | 4 +- libc/intrin/atomic_load.c | 53 --- libc/intrin/atomic_load.h | 22 -- libc/intrin/atomic_store.c | 54 --- libc/intrin/atomic_store.h | 23 -- .../{closehandle.greg.c => closehandle.c} | 0 ...eatedirectory.greg.c => createdirectory.c} | 0 .../{createfile.greg.c => createfile.c} | 0 ...filemapping.greg.c => createfilemapping.c} | 0 ...ingnuma.greg.c => createfilemappingnuma.c} | 0 ...eatenamedpipe.greg.c => createnamedpipe.c} | 0 .../{createpipe.greg.c => createpipe.c} | 0 .../{createprocess.greg.c => createprocess.c} | 0 ...mboliclink.greg.c => createsymboliclink.c} | 0 .../{createthread.greg.c => createthread.c} | 0 .../{deletefile.greg.c => deletefile.c} | 0 libc/intrin/describeflags.internal.h | 2 + libc/intrin/describeframe.c | 12 +- libc/intrin/describemapflags.greg.c | 2 +- libc/intrin/{findclose.greg.c => findclose.c} | 0 .../{findfirstfile.greg.c => findfirstfile.c} | 0 .../{findnextfile.greg.c => findnextfile.c} | 0 ...hfilebuffers.greg.c => flushfilebuffers.c} | 0 ...ushviewoffile.greg.c => flushviewoffile.c} | 0 libc/intrin/ftrace.c | 23 +- ...vent.greg.c => generateconsolectrlevent.c} | 0 ...odeprocess.greg.c => getexitcodeprocess.c} | 0 ...eattributes.greg.c => getfileattributes.c} | 0 libc/intrin/intrin.mk | 61 +-- libc/intrin/kopenflags.S | 1 + ...pviewoffileex.greg.c => mapviewoffileex.c} | 0 ...ileexnuma.greg.c => mapviewoffileexnuma.c} | 0 .../{movefileex.greg.c => movefileex.c} | 0 .../{openprocess.greg.c => openprocess.c} | 0 ...movedirectory.greg.c => removedirectory.c} | 0 .../{reopenfile.greg.c => reopenfile.c} | 0 libc/intrin/sched_yield.S | 54 +++ ...directory.greg.c => setcurrentdirectory.c} | 0 libc/intrin/spinlock.h | 26 +- libc/intrin/stracef.greg.c | 1 + ...inateprocess.greg.c => terminateprocess.c} | 0 ...mapviewoffile.greg.c => unmapviewoffile.c} | 0 ...virtualprotect.greg.c => virtualprotect.c} | 0 ...bjects.greg.c => waitformultipleobjects.c} | 0 ...gleobject.greg.c => waitforsingleobject.c} | 0 libc/isystem/stdatomic.h | 4 + libc/log/backtrace2.greg.c | 14 +- libc/log/checkfail.c | 4 +- libc/log/log.h | 76 ++-- libc/log/log.mk | 1 - libc/log/oncrash.c | 13 +- libc/log/showcrashreports.c | 23 +- libc/mem/putenv.c | 6 +- libc/runtime/arememoryintervalsok.c | 7 + libc/runtime/fork-nt.c | 2 - libc/runtime/ftrace-hook.S | 2 +- libc/runtime/ftraceinit.greg.c | 2 +- libc/runtime/ftracer.c | 64 ++-- libc/runtime/memtrack.greg.c | 63 +++- libc/runtime/memtrack.internal.h | 24 +- libc/runtime/munmap.c | 152 +++++--- libc/runtime/printargs.greg.c | 8 +- libc/runtime/printmemoryintervals.c | 10 +- libc/runtime/runtime.h | 7 +- libc/runtime/runtime.mk | 19 +- libc/runtime/stack.h | 7 +- libc/runtime/stackuse.c | 34 +- libc/runtime/untrackmemoryintervals.c | 9 +- libc/runtime/winmain.greg.c | 2 +- libc/sock/ntstdin.greg.c | 150 -------- libc/sock/ntstdin.internal.h | 24 -- libc/sock/sock.mk | 5 - libc/sysv/strace.greg.c | 26 +- libc/sysv/{g_syscount.S => syscount.S} | 18 +- libc/sysv/sysv.mk | 6 +- libc/testlib/testmain.c | 1 + libc/thread/sem.c | 7 +- libc/x/makedirs.c | 8 +- test/libc/calls/commandv_test.c | 8 +- test/libc/calls/mkdir_test.c | 45 ++- test/libc/fmt/lengthuint64_test.c | 12 +- test/libc/intrin/kprintf_test.c | 7 +- test/libc/rand/rand64_test.c | 17 +- test/libc/runtime/memtrack_test.c | 98 +++-- test/libc/runtime/mmap_test.c | 5 + test/libc/runtime/munmap_test.c | 232 ++++++++++++ test/libc/str/undeflate_test.c | 8 +- test/tool/plinko/plinko_test.c | 8 +- .../dlmalloc/{dlmalloc.greg.c => dlmalloc.c} | 9 +- third_party/dlmalloc/dlmalloc.mk | 11 +- third_party/dlmalloc/vespene.greg.c | 2 +- third_party/python/Include/ceval.h | 2 +- .../python/Lib/test/test_coroutines.py | 5 +- third_party/python/Lib/test/test_signal.py | 14 +- third_party/python/Python/cosmomodule.c | 6 +- third_party/python/python.c | 18 +- third_party/python/python.mk | 8 +- third_party/python/pythontester.c | 19 + third_party/python/repl.c | 351 +---------------- third_party/python/runpythonmodule.c | 356 ++++++++++++++++++ third_party/python/runpythonmodule.h | 10 + tool/args/args.c | 10 +- tool/emacs/cosmo-c-keywords.el | 3 +- tool/net/counters.inc | 2 + tool/net/help.txt | 9 +- tool/net/redbean.c | 135 ++++--- tool/plinko/lib/plinko.c | 4 +- tool/plinko/lib/printf.c | 4 +- tool/plinko/lib/read.c | 4 +- tool/plinko/plinko.c | 1 + .../viz/echoctl.c | 16 +- .../sched_yield-nt.c => tool/viz/vdsodump.c | 75 +++- 141 files changed, 1948 insertions(+), 1411 deletions(-) create mode 100644 libc/atomic.h create mode 100644 libc/bits/atomic-clang.h create mode 100644 libc/bits/atomic-gcc.h create mode 100644 libc/bits/atomic-gcc47.h rename libc/{sock/stdinworker.c => calls/describesigaltstack.greg.c} (71%) rename libc/calls/{sched_yield.c => nosync.c} (77%) delete mode 100644 libc/intrin/atomic_load.c delete mode 100644 libc/intrin/atomic_load.h delete mode 100644 libc/intrin/atomic_store.c delete mode 100644 libc/intrin/atomic_store.h rename libc/intrin/{closehandle.greg.c => closehandle.c} (100%) rename libc/intrin/{createdirectory.greg.c => createdirectory.c} (100%) rename libc/intrin/{createfile.greg.c => createfile.c} (100%) rename libc/intrin/{createfilemapping.greg.c => createfilemapping.c} (100%) rename libc/intrin/{createfilemappingnuma.greg.c => createfilemappingnuma.c} (100%) rename libc/intrin/{createnamedpipe.greg.c => createnamedpipe.c} (100%) rename libc/intrin/{createpipe.greg.c => createpipe.c} (100%) rename libc/intrin/{createprocess.greg.c => createprocess.c} (100%) rename libc/intrin/{createsymboliclink.greg.c => createsymboliclink.c} (100%) rename libc/intrin/{createthread.greg.c => createthread.c} (100%) rename libc/intrin/{deletefile.greg.c => deletefile.c} (100%) rename libc/intrin/{findclose.greg.c => findclose.c} (100%) rename libc/intrin/{findfirstfile.greg.c => findfirstfile.c} (100%) rename libc/intrin/{findnextfile.greg.c => findnextfile.c} (100%) rename libc/intrin/{flushfilebuffers.greg.c => flushfilebuffers.c} (100%) rename libc/intrin/{flushviewoffile.greg.c => flushviewoffile.c} (100%) rename libc/intrin/{generateconsolectrlevent.greg.c => generateconsolectrlevent.c} (100%) rename libc/intrin/{getexitcodeprocess.greg.c => getexitcodeprocess.c} (100%) rename libc/intrin/{getfileattributes.greg.c => getfileattributes.c} (100%) rename libc/intrin/{mapviewoffileex.greg.c => mapviewoffileex.c} (100%) rename libc/intrin/{mapviewoffileexnuma.greg.c => mapviewoffileexnuma.c} (100%) rename libc/intrin/{movefileex.greg.c => movefileex.c} (100%) rename libc/intrin/{openprocess.greg.c => openprocess.c} (100%) rename libc/intrin/{removedirectory.greg.c => removedirectory.c} (100%) rename libc/intrin/{reopenfile.greg.c => reopenfile.c} (100%) create mode 100644 libc/intrin/sched_yield.S rename libc/intrin/{setcurrentdirectory.greg.c => setcurrentdirectory.c} (100%) rename libc/intrin/{terminateprocess.greg.c => terminateprocess.c} (100%) rename libc/intrin/{unmapviewoffile.greg.c => unmapviewoffile.c} (100%) rename libc/intrin/{virtualprotect.greg.c => virtualprotect.c} (100%) rename libc/intrin/{waitformultipleobjects.greg.c => waitformultipleobjects.c} (100%) rename libc/intrin/{waitforsingleobject.greg.c => waitforsingleobject.c} (100%) create mode 100644 libc/isystem/stdatomic.h delete mode 100644 libc/sock/ntstdin.greg.c delete mode 100644 libc/sock/ntstdin.internal.h rename libc/sysv/{g_syscount.S => syscount.S} (91%) create mode 100644 test/libc/runtime/munmap_test.c rename third_party/dlmalloc/{dlmalloc.greg.c => dlmalloc.c} (99%) create mode 100644 third_party/python/pythontester.c create mode 100644 third_party/python/runpythonmodule.c create mode 100644 third_party/python/runpythonmodule.h rename libc/calls/getfdhandleactual.greg.c => tool/viz/echoctl.c (86%) rename libc/calls/sched_yield-nt.c => tool/viz/vdsodump.c (53%) diff --git a/build/htags b/build/htags index b5957145f..f1ffc2dd0 100755 --- a/build/htags +++ b/build/htags @@ -43,6 +43,10 @@ # '(progn # (add-hook 'c-mode-common-hook 'jart-c-mode-common-hook))) +# ctags doesn't understand atomics, e.g. +# extern char **environ; +set -- --regex-c='/_Atomic(\([^)]*\))/\1/b' "$@" + # ctags doesn't understand variable prototypes, e.g. # extern char **environ; set -- --regex-c='/^\(\(hidden\|extern\|const\) \)*[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)/\4/b' "$@" diff --git a/examples/greenbean.c b/examples/greenbean.c index 45e54f612..00d2fd4e8 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -24,6 +24,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" #include "libc/str/str.h" @@ -268,9 +269,9 @@ int main(int argc, char *argv[]) { workers = threads; for (i = 0; i < threads; ++i) { char *tls = __initialize_tls(malloc(64)); - void *stack = mmap(0, 65536, PROT_READ | PROT_WRITE, + void *stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); - CHECK_NE(-1, clone(Worker, stack, 65536, + CHECK_NE(-1, clone(Worker, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SETTLS, (void *)(intptr_t)i, 0, tls, 64, 0)); diff --git a/libc/atomic.h b/libc/atomic.h new file mode 100644 index 000000000..10e5925a9 --- /dev/null +++ b/libc/atomic.h @@ -0,0 +1,80 @@ +#ifndef COSMOPOLITAN_LIBC_ATOMIC_H_ +#define COSMOPOLITAN_LIBC_ATOMIC_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/** + * @fileoverview C11 Atomic Types + * + * We supoprt C++ and old C compilers. It's recommended you use macros + * like `_Atomic(int)` rather than `_Atomic int` or `atomic_int` since + * we only define a portability macro for the syntax `_Atomic(T)`. + * + * @see libc/integral/c.inc + * @see libc/bits/atomic.h + */ + +#define atomic_bool _Atomic(_Bool) +#define atomic_bool32 atomic_int32 +#define atomic_char _Atomic(char) +#define atomic_schar _Atomic(signed char) +#define atomic_uchar _Atomic(unsigned char) +#define atomic_short _Atomic(short) +#define atomic_ushort _Atomic(unsigned short) +#define atomic_int _Atomic(int) +#define atomic_uint _Atomic(unsigned int) +#define atomic_long _Atomic(long) +#define atomic_ulong _Atomic(unsigned long) +#define atomic_llong _Atomic(long long) +#define atomic_ullong _Atomic(unsigned long long) +#define atomic_char16_t _Atomic(char16_t) +#define atomic_char32_t _Atomic(char32_t) +#define atomic_wchar_t _Atomic(wchar_t) +#define atomic_intptr_t _Atomic(intptr_t) +#define atomic_uintptr_t _Atomic(uintptr_t) +#define atomic_size_t _Atomic(size_t) +#define atomic_ptrdiff_t _Atomic(ptrdiff_t) +#define atomic_int_fast8_t _Atomic(int_fast8_t) +#define atomic_uint_fast8_t _Atomic(uint_fast8_t) +#define atomic_int_fast16_t _Atomic(int_fast16_t) +#define atomic_uint_fast16_t _Atomic(uint_fast16_t) +#define atomic_int_fast32_t _Atomic(int_fast32_t) +#define atomic_uint_fast32_t _Atomic(uint_fast32_t) +#define atomic_int_fast64_t _Atomic(int_fast64_t) +#define atomic_uint_fast64_t _Atomic(uint_fast64_t) +#define atomic_int_least8_t _Atomic(int_least8_t) +#define atomic_uint_least8_t _Atomic(uint_least8_t) +#define atomic_int_least16_t _Atomic(int_least16_t) +#define atomic_uint_least16_t _Atomic(uint_least16_t) +#define atomic_int_least32_t _Atomic(int_least32_t) +#define atomic_uint_least32_t _Atomic(uint_least32_t) +#define atomic_int_least64_t _Atomic(int_least64_t) +#define atomic_uint_least64_t _Atomic(uint_least64_t) + +#ifdef __CLANG_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_BOOL_LOCK_FREE __CLANG_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __CLANG_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __CLANG_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __CLANG_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE +#else +#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE +#endif + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_ATOMIC_H_ */ diff --git a/libc/bits/atomic-clang.h b/libc/bits/atomic-clang.h new file mode 100644 index 000000000..6debe5e0d --- /dev/null +++ b/libc/bits/atomic-clang.h @@ -0,0 +1,36 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ +#define COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § atomics » clang ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define atomic_init(obj, value) __c11_atomic_init(obj, value) +#define atomic_thread_fence(order) __c11_atomic_thread_fence(order) +#define atomic_signal_fence(order) __c11_atomic_signal_fence(order) +#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ + success, failure) \ + __c11_atomic_compare_exchange_strong(object, expected, desired, success, \ + failure) +#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ + success, failure) \ + __c11_atomic_compare_exchange_weak(object, expected, desired, success, \ + failure) +#define atomic_exchange_explicit(object, desired, order) \ + __c11_atomic_exchange(object, desired, order) +#define atomic_fetch_add_explicit(object, operand, order) \ + __c11_atomic_fetch_add(object, operand, order) +#define atomic_fetch_and_explicit(object, operand, order) \ + __c11_atomic_fetch_and(object, operand, order) +#define atomic_fetch_or_explicit(object, operand, order) \ + __c11_atomic_fetch_or(object, operand, order) +#define atomic_fetch_sub_explicit(object, operand, order) \ + __c11_atomic_fetch_sub(object, operand, order) +#define atomic_fetch_xor_explicit(object, operand, order) \ + __c11_atomic_fetch_xor(object, operand, order) +#define atomic_load_explicit(object, order) __c11_atomic_load(object, order) +#define atomic_store_explicit(object, desired, order) \ + __c11_atomic_store(object, desired, order) + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ */ diff --git a/libc/bits/atomic-gcc.h b/libc/bits/atomic-gcc.h new file mode 100644 index 000000000..afc0c7458 --- /dev/null +++ b/libc/bits/atomic-gcc.h @@ -0,0 +1,58 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ +#define COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § atomics » old gnu ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define atomic_init(obj, value) ((void)(*(obj) = (value))) +#define atomic_thread_fence(order) __sync_synchronize() +#define atomic_signal_fence(order) __asm__ volatile("" ::: "memory") +#define __atomic_apply_stride(object, operand) \ + (((__typeof__(__atomic_val(object)))0) + (operand)) +#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ + success, failure) \ + __extension__({ \ + __typeof__(expected) __ep = (expected); \ + __typeof__(*__ep) __e = *__ep; \ + (void)(success); \ + (void)(failure); \ + (_Bool)((*__ep = __sync_val_compare_and_swap(object, __e, desired)) == \ + __e); \ + }) +#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ + success, failure) \ + atomic_compare_exchange_strong_explicit(object, expected, desired, success, \ + failure) +#if __has_builtin(__sync_swap) +#define atomic_exchange_explicit(object, desired, order) \ + ((void)(order), __sync_swap(object, desired)) +#else +#define atomic_exchange_explicit(object, desired, order) \ + __extension__({ \ + __typeof__(object) __o = (object); \ + __typeof__(desired) __d = (desired); \ + (void)(order); \ + __sync_synchronize(); \ + __sync_lock_test_and_set(&__atomic_val(__o), __d); \ + }) +#endif +#define atomic_fetch_add_explicit(object, operand, order) \ + ((void)(order), \ + __sync_fetch_and_add(object, __atomic_apply_stride(object, operand))) +#define atomic_fetch_and_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_and(object, operand)) +#define atomic_fetch_or_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_or(object, operand)) +#define atomic_fetch_sub_explicit(object, operand, order) \ + ((void)(order), \ + __sync_fetch_and_sub(object, __atomic_apply_stride(object, operand))) +#define atomic_fetch_xor_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_xor(object, operand)) +#define atomic_load_explicit(object, order) \ + ((void)(order), __sync_fetch_and_add(object, 0)) +#define atomic_store_explicit(object, desired, order) \ + ((void)atomic_exchange_explicit(object, desired, order)) + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ */ diff --git a/libc/bits/atomic-gcc47.h b/libc/bits/atomic-gcc47.h new file mode 100644 index 000000000..47928ec75 --- /dev/null +++ b/libc/bits/atomic-gcc47.h @@ -0,0 +1,34 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ +#define COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § atomics » gcc 4.7+ ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define atomic_init(obj, value) ((void)(*(obj) = (value))) +#define atomic_thread_fence(order) __atomic_thread_fence(order) +#define atomic_signal_fence(order) __atomic_signal_fence(order) +#define atomic_compare_exchange_strong_explicit(pObject, pExpected, desired, \ + success, failure) \ + __atomic_compare_exchange_n(pObject, pExpected, desired, 0, success, failure) +#define atomic_compare_exchange_weak_explicit(pObject, pExpected, desired, \ + success, failure) \ + __atomic_compare_exchange_n(pObject, pExpected, desired, 1, success, failure) +#define atomic_exchange_explicit(pObject, desired, order) \ + __atomic_exchange_n(pObject, desired, order) +#define atomic_fetch_add_explicit(pObject, operand, order) \ + __atomic_fetch_add(pObject, operand, order) +#define atomic_fetch_and_explicit(pObject, operand, order) \ + __atomic_fetch_and(pObject, operand, order) +#define atomic_fetch_or_explicit(pObject, operand, order) \ + __atomic_fetch_or(pObject, operand, order) +#define atomic_fetch_sub_explicit(pObject, operand, order) \ + __atomic_fetch_sub(pObject, operand, order) +#define atomic_fetch_xor_explicit(pObject, operand, order) \ + __atomic_fetch_xor(pObject, operand, order) +#define atomic_load_explicit(pObject, order) __atomic_load_n(pObject, order) +#define atomic_store_explicit(pObject, desired, order) \ + __atomic_store_n(pObject, desired, order) + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ */ diff --git a/libc/bits/atomic.h b/libc/bits/atomic.h index e2d628adf..d2073d886 100644 --- a/libc/bits/atomic.h +++ b/libc/bits/atomic.h @@ -1,18 +1,18 @@ #ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ #define COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ -#include "libc/bits/bits.h" -#include "libc/intrin/lockcmpxchg.h" +#include "libc/atomic.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ /** - * @fileoverview C11 version of The Cosmopolitan Atomics Library. + * @fileoverview Cosmopolitan C11 Atomics Library * * - Forty-two different ways to say MOV. * - Fourteen different ways to say XCHG. * - Twenty different ways to say LOCK CMPXCHG. * - * Living proof high-level languages can be lower-level than assembly. + * It's a lower level programming language than assembly! + * + * @see libc/atomic.h */ #define memory_order int @@ -23,31 +23,47 @@ COSMOPOLITAN_C_START_ #define memory_order_acq_rel 4 #define memory_order_seq_cst 5 -#define atomic_flag struct AtomicFlag -#define atomic_flag_clear(PTR) atomic_store((PTR)->__cacheline, 0) -#define atomic_flag_test_and_set(PTR) \ - ({ \ - uint32_t ax = 0; \ - lockcmpxchg((PTR)->__cacheline, &ax, 1); \ - }) -#define atomic_init(PTR, VAL) atomic_store(PTR, VAL) -#define atomic_exchange(PTR, VAL) lockxchg(PTR, &(VAL)) -#define atomic_compare_exchange_strong(X, Y, Z) _lockcmpxchg(X, Y, Z) -#define atomic_compare_exchange_weak(X, Y, Z) _lockcmpxchg(X, Y, Z) -#define atomic_load_explicit(PTR, ORDER) atomic_load(PTR) -#define atomic_store_explicit(PTR, VAL, ORDER) atomic_store(PTR, VAL) -#define atomic_flag_clear_explicit(PTR, ORDER) atomic_store(PTR, 0) -#define atomic_exchange_explicit(PTR, VAL, ORDER) lockxchg(PTR, &(VAL)) -#define atomic_flag_test_and_set_explicit(PTR, ORDER) lockcmpxchg(PTR, 0, 1) -#define atomic_compare_exchange_strong_explicit(X, Y, Z, S, F) \ - lockcmpxchg(X, Y, Z) -#define atomic_compare_exchange_weak_explicit(X, Y, Z, S, F) \ - lockcmpxchg(X, Y, Z) +#define ATOMIC_VAR_INIT(value) (value) +#define atomic_is_lock_free(obj) ((void)(obj), sizeof(obj) <= sizeof(void *)) -struct AtomicFlag { - uint32_t __cacheline[16]; /* Intel V.O §9.4.6 */ -} forcealign(64); +#define atomic_flag atomic_bool +#define ATOMIC_FLAG_INIT ATOMIC_VAR_INIT(0) +#define atomic_flag_test_and_set_explicit(x, order) \ + atomic_exchange_explicit(x, 1, order) +#define atomic_flag_clear_explicit(x, order) atomic_store_explicit(x, 0, order) + +#define atomic_compare_exchange_strong(pObject, pExpected, desired) \ + atomic_compare_exchange_strong_explicit( \ + pObject, pExpected, desired, memory_order_seq_cst, memory_order_seq_cst) +#define atomic_compare_exchange_weak(pObject, pExpected, desired) \ + atomic_compare_exchange_weak_explicit( \ + pObject, pExpected, desired, memory_order_seq_cst, memory_order_seq_cst) +#define atomic_exchange(pObject, desired) \ + atomic_exchange_explicit(pObject, desired, memory_order_seq_cst) +#define atomic_fetch_add(pObject, operand) \ + atomic_fetch_add_explicit(pObject, operand, memory_order_seq_cst) +#define atomic_fetch_and(pObject, operand) \ + atomic_fetch_and_explicit(pObject, operand, memory_order_seq_cst) +#define atomic_fetch_or(pObject, operand) \ + atomic_fetch_or_explicit(pObject, operand, memory_order_seq_cst) +#define atomic_fetch_sub(pObject, operand) \ + atomic_fetch_sub_explicit(pObject, operand, memory_order_seq_cst) +#define atomic_fetch_xor(pObject, operand) \ + atomic_fetch_xor_explicit(pObject, operand, memory_order_seq_cst) +#define atomic_load(pObject) atomic_load_explicit(pObject, memory_order_seq_cst) +#define atomic_store(pObject, desired) \ + atomic_store_explicit(pObject, desired, memory_order_seq_cst) +#define atomic_flag_test_and_set(x) \ + atomic_flag_test_and_set_explicit(x, memory_order_seq_cst) +#define atomic_flag_clear(x) atomic_flag_clear_explicit(x, memory_order_seq_cst) + +#if defined(__CLANG_ATOMIC_BOOL_LOCK_FREE) +#include "libc/bits/atomic-clang.h" +#elif (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 +#include "libc/bits/atomic-gcc47.h" +#else +#include "libc/bits/atomic-gcc.h" +#endif -COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ */ diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index c238eb5c1..1845696bf 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,31 +65,39 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/calls/sigenter-freebsd.o \ -o/$(MODE)/libc/calls/sigenter-netbsd.o \ -o/$(MODE)/libc/calls/sigenter-openbsd.o \ +# we can't use asan because: +# ucontext_t memory is owned by xnu kernel o/$(MODE)/libc/calls/sigenter-xnu.o: \ OVERRIDE_COPTS += \ - -mno-fentry \ - -fno-stack-protector \ - -fno-sanitize=all + -ffreestanding \ + -fno-sanitize=address -o/$(MODE)/libc/calls/sys_mprotect.greg.o \ -o/$(MODE)/libc/calls/vdsofunc.greg.o \ -o/$(MODE)/libc/calls/directmap.o \ -o/$(MODE)/libc/calls/directmap-nt.o \ -o/$(MODE)/libc/calls/mapstack.greg.o \ -o/$(MODE)/libc/calls/execve-nt.greg.o \ -o/$(MODE)/libc/calls/getcwd.greg.o \ -o/$(MODE)/libc/calls/getcwd-xnu.greg.o \ -o/$(MODE)/libc/calls/getprogramexecutablename.greg.o \ -o/$(MODE)/libc/calls/raise.o: \ +# we can't use asan because: +# vdso memory is owned by linux kernel +o/$(MODE)/libc/calls/vdsofunc.greg.o: \ OVERRIDE_COPTS += \ -ffreestanding \ - $(NO_MAGIC) + -fno-sanitize=address -o/$(MODE)/libc/calls/termios2linux.o \ -o/$(MODE)/libc/calls/termios2host.o \ +# we can't use asan because: +# asan guard pages haven't been allocated yet +o/$(MODE)/libc/calls/directmap.o \ +o/$(MODE)/libc/calls/directmap-nt.o: \ + OVERRIDE_COPTS += \ + -ffreestanding \ + -fno-sanitize=address + +# 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: \ + OVERRIDE_COPTS += \ + -ffreestanding \ + -fno-sanitize=address + +# we always want -O3 because: +# it makes the code size smaller too o/$(MODE)/libc/calls/sigenter-freebsd.o \ o/$(MODE)/libc/calls/sigenter-netbsd.o \ o/$(MODE)/libc/calls/sigenter-openbsd.o \ @@ -98,7 +106,36 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: \ OVERRIDE_COPTS += \ -O3 -# TODO(jart): make va_arg optimize well in default mode +# 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/execve-sysv.o \ +o/$(MODE)/libc/calls/mkntenvblock.o: \ + OVERRIDE_CPPFLAGS += \ + -DSTACK_FRAME_UNLIMITED + +# we must disable static stack safety because: +# PATH_MAX*sizeof(char16_t)*2 exceeds 4096 byte frame limit +o/$(MODE)/libc/calls/copyfile.o \ +o/$(MODE)/libc/calls/symlinkat-nt.o \ +o/$(MODE)/libc/calls/readlinkat-nt.o \ +o/$(MODE)/libc/calls/linkat-nt.o \ +o/$(MODE)/libc/calls/renameat-nt.o: \ + OVERRIDE_CPPFLAGS += \ + -DSTACK_FRAME_UNLIMITED + +# we must segregate codegen because: +# file contains multiple independently linkable apis +o/$(MODE)/libc/calls/ioctl-siocgifconf.o \ +o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: \ + OVERRIDE_COPTS += \ + -ffunction-sections \ + -fdata-sections + +# we always want -Os because: +# va_arg codegen is very bloated in default mode o//libc/calls/open.o \ o//libc/calls/openat.o \ o//libc/calls/prctl.o \ @@ -120,27 +157,6 @@ o//libc/calls/fcntl.o: \ OVERRIDE_CFLAGS += \ -Os -# must use alloca() or path_max*2*2 -o/$(MODE)/libc/calls/execl.o \ -o/$(MODE)/libc/calls/execle.o \ -o/$(MODE)/libc/calls/execlp.o \ -o/$(MODE)/libc/calls/copyfile.o \ -o/$(MODE)/libc/calls/execve-nt.greg.o \ -o/$(MODE)/libc/calls/linkat-nt.o \ -o/$(MODE)/libc/calls/renameat-nt.o \ -o/$(MODE)/libc/calls/execve-sysv.o \ -o/$(MODE)/libc/calls/symlinkat-nt.o \ -o/$(MODE)/libc/calls/readlinkat-nt.o \ -o/$(MODE)/libc/calls/mkntenvblock.o: \ - OVERRIDE_CPPFLAGS += \ - -DSTACK_FRAME_UNLIMITED - -o/$(MODE)/libc/calls/ioctl-siocgifconf.o \ -o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: \ - OVERRIDE_COPTS += \ - -ffunction-sections \ - -fdata-sections - LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x))) LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS)) LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index a6270628a..ef9bfe2fd 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -21,7 +21,6 @@ #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" -#include "libc/sock/ntstdin.internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -42,12 +41,7 @@ textwindows int sys_close_nt(struct Fd *fd) { // if this file descriptor is wrapped in a named pipe worker thread // then we need to close our copy of the worker thread handle. it's // also required that whatever install a worker use malloc, so free - if (fd->worker) { - if (!weaken(UnrefNtStdinWorker)(fd->worker)) ok = false; - fd->worker = 0; - } else { - if (!CloseHandle(fd->handle)) ok = false; - } + if (!CloseHandle(fd->handle)) ok = false; if (fd->kind == kFdConsole && fd->extra && fd->extra != -1) { if (!CloseHandle(fd->extra)) ok = false; } diff --git a/libc/sock/stdinworker.c b/libc/calls/describesigaltstack.greg.c similarity index 71% rename from libc/sock/stdinworker.c rename to libc/calls/describesigaltstack.greg.c index 0dc32fe94..dbfc830cb 100644 --- a/libc/sock/stdinworker.c +++ b/libc/calls/describesigaltstack.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 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 │ @@ -16,29 +16,22 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/calls/strace.internal.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/ntstdin.internal.h" -#include "libc/sock/sock.h" +#include "libc/calls/struct/sigaltstack.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/kprintf.h" -/* STATIC_YOINK("StdinWorker"); */ - -static textexit void StdinWorkerFree(void) { - int i; - NTTRACE("StdinWorkerFree()"); - for (i = g_fds.n; i--;) { - if (g_fds.p[i].kind && g_fds.p[i].worker) { - close(i); - } +const char *DescribeSigaltstk(char *buf, size_t bufsize, int rc, + const struct sigaltstack *ss) { + if (rc == -1) return "n/a"; + if (!ss) return "NULL"; + if ((!IsAsan() && kisdangerous(ss)) || + (IsAsan() && !__asan_is_valid(ss, sizeof(*ss)))) { + ksnprintf(buf, sizeof(buf), "%p", ss); + } else { + ksnprintf(buf, bufsize, "{.ss_sp=%p, .ss_flags=%#lx, .ss_size=%'zu}", + ss->ss_sp, ss->ss_flags, ss->ss_size); } + return buf; } - -static textstartup void StdinWorkerInit(void) { - g_fds.p[0].worker = NewNtStdinWorker(0); - atexit(StdinWorkerFree); -} - -const void *const StdinWorker[] initarray = { - StdinWorkerInit, -}; diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 865e28025..7fcfff765 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -25,7 +25,6 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/sock/internal.h" -#include "libc/sock/ntstdin.internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -67,16 +66,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { g_fds.p[newfd].kind = kFdReserved; } - // if this file descriptor is wrapped in a named pipe worker thread - // then we should clone the original authentic handle rather than the - // stdin worker's named pipe. we won't clone the worker, since that - // can always be recreated again on demand. - if (g_fds.p[oldfd].worker) { - handle = g_fds.p[oldfd].worker->reader; - } else { - handle = g_fds.p[oldfd].handle; - } - + handle = g_fds.p[oldfd].handle; proc = GetCurrentProcess(); if (DuplicateHandle(proc, handle, proc, &g_fds.p[newfd].handle, 0, true, kNtDuplicateSameAccess)) { @@ -90,9 +80,6 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } else { g_fds.p[newfd].extra = g_fds.p[oldfd].extra; } - if (g_fds.p[oldfd].worker) { - g_fds.p[newfd].worker = weaken(RefNtStdinWorker)(g_fds.p[oldfd].worker); - } rc = newfd; } else { __releasefd(newfd); diff --git a/libc/calls/fdatasync.c b/libc/calls/fdatasync.c index 2f63c4281..6e72c1c2c 100644 --- a/libc/calls/fdatasync.c +++ b/libc/calls/fdatasync.c @@ -20,21 +20,28 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/runtime/runtime.h" /** * Blocks until kernel flushes non-metadata buffers for fd to disk. * * @return 0 on success, or -1 w/ errno - * @see fsync(), sync_file_range() + * @see sync(), fsync(), sync_file_range() + * @see __nosync to secretly disable * @asyncsignalsafe */ int fdatasync(int fd) { int rc; - if (!IsWindows()) { - rc = sys_fdatasync(fd); + if (__nosync != 0x5453455454534146) { + if (!IsWindows()) { + rc = sys_fdatasync(fd); + } else { + rc = sys_fdatasync_nt(fd); + } + STRACE("fdatasync(%d) → %d% m", fd, rc); } else { - rc = sys_fdatasync_nt(fd); + rc = 0; + STRACE("fdatasync(%d) → disabled% m", fd); } - STRACE("%s(%d) → %d% m", "fdatasync", fd, rc); return rc; } diff --git a/libc/calls/fsync.c b/libc/calls/fsync.c index 99b6286ec..e3b251fbf 100644 --- a/libc/calls/fsync.c +++ b/libc/calls/fsync.c @@ -20,21 +20,28 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/runtime/runtime.h" /** * Blocks until kernel flushes buffers for fd to disk. * * @return 0 on success, or -1 w/ errno * @see fdatasync(), sync_file_range() + * @see __nosync to secretly disable * @asyncsignalsafe */ int fsync(int fd) { int rc; - if (!IsWindows()) { - rc = sys_fsync(fd); + if (__nosync != 0x5453455454534146) { + if (!IsWindows()) { + rc = sys_fsync(fd); + } else { + rc = sys_fdatasync_nt(fd); + } + STRACE("fysnc(%d) → %d% m", fd, rc); } else { - rc = sys_fdatasync_nt(fd); + rc = 0; + STRACE("fsync(%d) → disabled% m", fd); } - STRACE("%s(%d) → %d% m", "fsync", fd, rc); return rc; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 5c4457c67..9d7ab37ea 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -62,7 +62,6 @@ struct Fd { unsigned mode; int64_t handle; int64_t extra; - struct NtStdinWorker *worker; bool zombie; }; @@ -90,9 +89,12 @@ void __releasefd(int) hidden; void __releasefd_unlocked(int) hidden; int __ensurefds(int) hidden; int __ensurefds_unlocked(int) hidden; -int64_t __getfdhandleactual(int) hidden; void __printfds(void) hidden; +forceinline int64_t __getfdhandleactual(int fd) { + return g_fds.p[fd].handle; +} + forceinline bool __isfdopen(int fd) { return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty; } diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 29eacc9b1..372c392eb 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -31,7 +31,7 @@ } \ } while (0) -static noasan bool NeedsQuotes(const char *s) { +static bool NeedsQuotes(const char *s) { if (!*s) return true; do { if (*s == ' ' || *s == '\t') { @@ -54,8 +54,8 @@ static noasan bool NeedsQuotes(const char *s) { * @return freshly allocated lpCommandLine or NULL w/ errno * @see libc/runtime/dosargv.c */ -textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX / 2], - const char *prog, char *const argv[]) { +textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], const char *prog, + char *const argv[]) { char *arg; uint64_t w; wint_t x, y; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index 95287bb6d..f8315ad52 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -31,14 +31,14 @@ #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) -static noasan int CompareStrings(const char *l, const char *r) { +static int CompareStrings(const char *l, const char *r) { int a, b; size_t i = 0; while ((a = ToUpper(l[i] & 255)) == (b = ToUpper(r[i] & 255)) && r[i]) ++i; return a - b; } -static noasan void InsertString(char **a, size_t i, char *s) { +static void InsertString(char **a, size_t i, char *s) { size_t j; for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) { a[j] = a[j - 1]; @@ -57,8 +57,8 @@ static noasan void InsertString(char **a, size_t i, char *s) { * @return 0 on success, or -1 w/ errno * @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767) */ -textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX / 2], - char *const envp[], const char *extravar) { +textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[], + const char *extravar) { bool v; char *t; axdx_t rc; diff --git a/libc/calls/sched_yield.c b/libc/calls/nosync.c similarity index 77% rename from libc/calls/sched_yield.c rename to libc/calls/nosync.c index 04b744961..fd0a71df8 100644 --- a/libc/calls/sched_yield.c +++ b/libc/calls/nosync.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=8 sts=2 sw=2 fenc=utf-8 :vi│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -17,17 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" /** - * Asks kernel to deschedule thread momentarily. + * Tunes sync system call availability. + * + * If this value is set to 0x5453455454534146, then the system calls + * sync(), fsync(), and fdatasync() system calls will do nothing and + * return success. This is intended to be used for things like making + * things like Python unit tests go faster because fsync is extremely + * slow and using tmpfs requires root privileges. */ -int sched_yield(void) { - /* TODO(jart): Add get_sched_yield() so we can STRACE() */ - if (!IsWindows()) { - return sys_sched_yield(); - } else { - return sys_sched_yield_nt(); - } -} +uint64_t __nosync; diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index a9536e4c1..c1a282992 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -38,7 +38,6 @@ #include "libc/nt/synchronization.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" -#include "libc/sock/ntstdin.internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index 26ad273b2..d9ff0a691 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -54,7 +54,6 @@ void __printfds(void) { 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); if (g_fds.p[i].extra) kprintf(" extra=%ld", g_fds.p[i].extra); - if (g_fds.p[i].worker) kprintf(" worker=%p", g_fds.p[i].worker); kprintf("\n"); } _spunlock(&__fds_lock); diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index d35116378..4e90745ab 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -16,12 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -50,6 +52,7 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { (bytes = __zipos_notat(dirfd, path)) == -1) { STRACE("TOOD: zipos support for readlinkat"); } else if (!IsWindows()) { + assert(bufsiz); bytes = sys_readlinkat(dirfd, path, buf, bufsiz); } else { bytes = sys_readlinkat_nt(dirfd, path, buf, bufsiz); diff --git a/libc/calls/realpath.c b/libc/calls/realpath.c index cb79c7142..559529652 100644 --- a/libc/calls/realpath.c +++ b/libc/calls/realpath.c @@ -30,6 +30,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" #include "libc/errno.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" @@ -81,7 +82,9 @@ char *realpath(const char *filename, char *resolved) ssize_t rc; int e, up, check_dir=0; size_t k, p, q, l, l0, cnt=0, nup=0; - char output[PATH_MAX], stack[PATH_MAX], *z; + char output[PATH_MAX], stack[PATH_MAX+1], *z; + + /* STRACE("realpath(%#s, %#s)", filename, resolved); */ if (!filename) { einval(); diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c index 3f2a74eb0..4ec6f30f5 100644 --- a/libc/calls/sigaltstack.c +++ b/libc/calls/sigaltstack.c @@ -22,10 +22,11 @@ #include "libc/calls/struct/sigaltstack.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" -static noasan void sigaltstack2bsd(struct sigaltstack_bsd *bsd, - const struct sigaltstack *linux) { +static void sigaltstack2bsd(struct sigaltstack_bsd *bsd, + const struct sigaltstack *linux) { void *sp; int flags; size_t size; @@ -37,8 +38,8 @@ static noasan void sigaltstack2bsd(struct sigaltstack_bsd *bsd, bsd->ss_size = size; } -static noasan void sigaltstack2linux(struct sigaltstack *linux, - const struct sigaltstack_bsd *bsd) { +static void sigaltstack2linux(struct sigaltstack *linux, + const struct sigaltstack_bsd *bsd) { void *sp; int flags; size_t size; @@ -69,10 +70,11 @@ static noasan void sigaltstack2linux(struct sigaltstack *linux, * @param old if non-null will receive current signal alt stack * @return 0 on success, or -1 w/ errno */ -noasan int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { +int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { int rc; void *b; const void *a; + char buf[2][128]; struct sigaltstack_bsd bsd; if (IsAsan() && ((old && __asan_check(old, sizeof(*old)).kind) || (neu && (__asan_check(neu, sizeof(*neu)).kind || @@ -106,6 +108,8 @@ noasan int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { } else { rc = enosys(); } - STRACE("sigaltstack() → %d% m", rc); + STRACE("sigaltstack(%s, [%s]) → %d% m", + DescribeSigaltstk(buf[0], sizeof(buf[0]), 0, neu), + DescribeSigaltstk(buf[0], sizeof(buf[0]), 0, old), rc); return rc; } diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index fbfa984a6..b8d3cc662 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -4,6 +4,7 @@ #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" +#include "libc/runtime/runtime.h" #define _KERNTRACE 0 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ @@ -50,8 +51,6 @@ COSMOPOLITAN_C_START_ #define NTTRACE(FMT, ...) (void)0 #endif -extern int __strace; - void __stracef(const char *, ...); COSMOPOLITAN_C_END_ diff --git a/libc/calls/sync.c b/libc/calls/sync.c index 231ae339f..842df3762 100644 --- a/libc/calls/sync.c +++ b/libc/calls/sync.c @@ -18,15 +18,22 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" #include "libc/dce.h" /** * Flushes file system changes to disk by any means necessary. + * @see __nosync to secretly disable */ void sync(void) { - if (!IsWindows()) { - sys_sync(); + if (__nosync != 0x5453455454534146) { + if (!IsWindows()) { + sys_sync(); + } else { + sys_sync_nt(); + } + STRACE("sync()% m"); } else { - sys_sync_nt(); + STRACE("sync() → disabled% m"); } } diff --git a/libc/calls/weirdtypes.h b/libc/calls/weirdtypes.h index de7973ae1..569e66226 100644 --- a/libc/calls/weirdtypes.h +++ b/libc/calls/weirdtypes.h @@ -68,66 +68,5 @@ typedef __UINT_FAST64_TYPE__ uint_fast64_t; #define INT_FAST64_MIN (-INT_FAST64_MAX - 1) #define UINT_FAST16_MIN (-UINT_FAST16_MAX - 1) -#define atomic_bool _Atomic(_Bool) -#define atomic_bool32 atomic_int_fast32_t -#define atomic_char _Atomic(char) -#define atomic_schar _Atomic(signed char) -#define atomic_uchar _Atomic(unsigned char) -#define atomic_short _Atomic(short) -#define atomic_ushort _Atomic(unsigned short) -#define atomic_int _Atomic(int) -#define atomic_uint _Atomic(unsigned int) -#define atomic_long _Atomic(long) -#define atomic_ulong _Atomic(unsigned long) -#define atomic_llong _Atomic(long long) -#define atomic_ullong _Atomic(unsigned long long) -#define atomic_char16_t _Atomic(char16_t) -#define atomic_char32_t _Atomic(char32_t) -#define atomic_wchar_t _Atomic(wchar_t) -#define atomic_int_least8_t _Atomic(int_least8_t) -#define atomic_uint_least8_t _Atomic(uint_least8_t) -#define atomic_int_least16_t _Atomic(int_least16_t) -#define atomic_uint_least16_t _Atomic(uint_least16_t) -#define atomic_int_least32_t _Atomic(int_least32_t) -#define atomic_uint_least32_t _Atomic(uint_least32_t) -#define atomic_int_least64_t _Atomic(int_least64_t) -#define atomic_uint_least64_t _Atomic(uint_least64_t) -#define atomic_int_fast8_t _Atomic(int_fast8_t) -#define atomic_uint_fast8_t _Atomic(uint_fast8_t) -#define atomic_int_fast16_t _Atomic(int_fast16_t) -#define atomic_uint_fast16_t _Atomic(uint_fast16_t) -#define atomic_int_fast32_t _Atomic(int_fast32_t) -#define atomic_uint_fast32_t _Atomic(uint_fast32_t) -#define atomic_int_fast64_t _Atomic(int_fast64_t) -#define atomic_uint_fast64_t _Atomic(uint_fast64_t) -#define atomic_intptr_t _Atomic(intptr_t) -#define atomic_uintptr_t _Atomic(uintptr_t) -#define atomic_size_t _Atomic(size_t) -#define atomic_ptrdiff_t _Atomic(ptrdiff_t) - -#ifdef __CLANG_ATOMIC_BOOL_LOCK_FREE -#define ATOMIC_BOOL_LOCK_FREE __CLANG_ATOMIC_BOOL_LOCK_FREE -#define ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE -#define ATOMIC_CHAR16_T_LOCK_FREE __CLANG_ATOMIC_CHAR16_T_LOCK_FREE -#define ATOMIC_CHAR32_T_LOCK_FREE __CLANG_ATOMIC_CHAR32_T_LOCK_FREE -#define ATOMIC_WCHAR_T_LOCK_FREE __CLANG_ATOMIC_WCHAR_T_LOCK_FREE -#define ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE -#define ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE -#define ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE -#define ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE -#define ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE -#else -#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE -#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE -#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE -#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE -#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE -#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE -#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE -#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE -#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE -#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE -#endif - #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_WEIRDTYPES_H_ */ diff --git a/libc/integral/c.inc b/libc/integral/c.inc index c2186bdf3..57ea8b3d6 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -67,8 +67,8 @@ } while (0) #endif -#if __STDC_VERSION__ + 0 < 201112 && defined(__x86__) -#define _Atomic(TYPE) TYPE +#if __STDC_VERSION__ + 0 < 201112 +#define _Atomic(TYPE) TYPE volatile #endif #ifdef __llvm__ @@ -123,7 +123,7 @@ typedef __UINT64_TYPE__ uint64_t; typedef __INTMAX_TYPE__ intmax_t; typedef __UINTMAX_TYPE__ uintmax_t; -#if __GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__) +#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 406 || defined(__llvm__) typedef signed __int128 int128_t; typedef unsigned __int128 uint128_t; #endif diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 4cf6d3d06..c675eff38 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -50,6 +50,9 @@ #ifndef __has_cpp_attribute #define __has_cpp_attribute(x) 0 #endif +#ifndef __has_extension +#define __has_extension(x) 0 +#endif #ifdef unix #undef unix diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index d88aaa3db..7b17daa53 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -128,7 +128,7 @@ struct AsanSourceLocation { }; struct AsanAccessInfo { - const uintptr_t addr; + const char *addr; const uintptr_t first_bad_addr; size_t size; bool iswrite; @@ -136,7 +136,7 @@ struct AsanAccessInfo { }; struct AsanGlobal { - const uintptr_t addr; + const char *addr; size_t size; size_t size_with_redzone; const void *name; @@ -339,10 +339,10 @@ dontdiscard static __asan_die_f *__asan_die(void) { } } -void __asan_poison(long p, long n, signed char t) { +void __asan_poison(void *p, long n, signed char t) { signed char k, *s; - s = (signed char *)((p >> 3) + 0x7fff8000); - if ((k = p & 7)) { + s = (signed char *)(((intptr_t)p >> 3) + 0x7fff8000); + if ((k = (intptr_t)p & 7)) { if ((!*s && n >= 8 - k) || *s > k) *s = k; n -= MIN(8 - k, n); s += 1; @@ -354,10 +354,10 @@ void __asan_poison(long p, long n, signed char t) { } } -void __asan_unpoison(long p, long n) { +void __asan_unpoison(void *p, long n) { signed char k, *s; - k = p & 7; - s = (signed char *)((p >> 3) + 0x7fff8000); + k = (intptr_t)p & 7; + s = (signed char *)(((intptr_t)p >> 3) + 0x7fff8000); if (UNLIKELY(k)) { if (k + n < 8) { if (n > 0) *s = MAX(*s, k + n); @@ -737,9 +737,9 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, uint64_t x, y, z; char *p, *q, *base; struct MemoryIntervals *m; - ++g_ftrace; + --__ftrace; p = __fatalbuf; - kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n%s\n", + 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, SHADOW(addr), __argv[0]); if (0 < size && size < 80) { @@ -759,19 +759,19 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, for (c = i = 0; i < 80; ++i) { if (!(t = __asan_check(base + i, 1).kind)) { if (c != 32) { - p = __stpcpy(p, "\e[32m"); + *p++ = '\e', *p++ = '[', *p++ = '3', *p++ = '2', *p++ = 'm'; c = 32; } *p++ = '.'; } else { if (c != 31) { - p = __stpcpy(p, "\e[31m"); + *p++ = '\e', *p++ = '[', *p++ = '3', *p++ = '1', *p++ = 'm'; c = 31; } p = __asan_utf8cpy(p, __asan_symbolize_access_poison(t)); } } - p = __stpcpy(p, "\e[39m"); + *p++ = '\e', *p++ = '[', *p++ = '3', *p++ = '9', *p++ = 'm'; *p++ = '\n'; for (i = 0; (intptr_t)(base + i) & 7; ++i) *p++ = ' '; for (; i + 8 <= 80; i += 8) { @@ -814,7 +814,7 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, kprintf("%s", __fatalbuf); __asan_report_memory_origin(addr, size, kind); kprintf("\nthe crash was caused by\n"); - --g_ftrace; + ++__ftrace; return __asan_die(); } @@ -840,7 +840,7 @@ dontdiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size, void *__asan_morgue_add(void *p) { int i; void *r; - _spinlock_optimistic(&__asan_lock); + _spinlock_cooperative(&__asan_lock); i = __asan_morgue.i++ & (ARRAYLEN(__asan_morgue.p) - 1); r = __asan_morgue.p[i]; __asan_morgue.p[i] = p; @@ -851,7 +851,7 @@ void *__asan_morgue_add(void *p) { static void __asan_morgue_flush(void) { int i; void *p; - _spinlock_optimistic(&__asan_lock); + _spinlock_cooperative(&__asan_lock); for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { if (weaken(dlfree)) { weaken(dlfree)(__asan_morgue.p[i]); @@ -943,9 +943,9 @@ static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, if ((p = weaken(dlmemalign)(a, __asan_heap_size(n)))) { c = weaken(dlmalloc_usable_size)(p); e = (struct AsanExtra *)(p + c - sizeof(*e)); - __asan_unpoison((uintptr_t)p, n); - __asan_poison((uintptr_t)p - 16, 16, underrun); /* see dlmalloc design */ - __asan_poison((uintptr_t)p + n, c - n, overrun); + __asan_unpoison(p, n); + __asan_poison(p - 16, 16, underrun); /* see dlmalloc design */ + __asan_poison(p + n, c - n, overrun); __asan_memset(p, 0xF9, n); __asan_write48(&e->size, n); __asan_memcpy(&e->bt, bt, sizeof(*bt)); @@ -1030,7 +1030,7 @@ static void __asan_deallocate(char *p, long kind) { struct AsanExtra *e; if ((e = __asan_get_extra(p, &c))) { if (__asan_read48(e->size, &n)) { - __asan_poison((uintptr_t)p, c, kind); + __asan_poison(p, c, kind); if (c <= ASAN_MORGUE_THRESHOLD) { p = __asan_morgue_add(p); } @@ -1084,11 +1084,11 @@ static void *__asan_realloc_impl(void *p, size_t n, if ((e = __asan_get_extra(p, &c))) { if (__asan_read48(e->size, &m)) { if (n <= m) { /* shrink */ - __asan_poison((uintptr_t)p + n, m - n, kAsanHeapOverrun); + __asan_poison((char *)p + n, m - n, kAsanHeapOverrun); __asan_write48(&e->size, n); return p; } else if (n <= c - sizeof(struct AsanExtra)) { /* small growth */ - __asan_unpoison((uintptr_t)p + m, n - m); + __asan_unpoison((char *)p + m, n - m); __asan_write48(&e->size, n); return p; } else { /* exponential growth */ @@ -1175,13 +1175,12 @@ void __asan_handle_no_return(void) { void __asan_register_globals(struct AsanGlobal g[], int n) { int i; - __asan_poison((intptr_t)g, sizeof(*g) * n, kAsanProtected); + __asan_poison(g, sizeof(*g) * n, kAsanProtected); for (i = 0; i < n; ++i) { __asan_poison(g[i].addr + g[i].size, g[i].size_with_redzone - g[i].size, kAsanGlobalRedzone); if (g[i].location) { - __asan_poison((intptr_t)g[i].location, sizeof(*g[i].location), - kAsanProtected); + __asan_poison(g[i].location, sizeof(*g[i].location), kAsanProtected); } } } @@ -1228,15 +1227,15 @@ void __asan_report_store(uint8_t *addr, int size) { } } -void __asan_poison_stack_memory(uintptr_t addr, size_t size) { +void __asan_poison_stack_memory(char *addr, size_t size) { __asan_poison(addr, size, kAsanStackFree); } -void __asan_unpoison_stack_memory(uintptr_t addr, size_t size) { +void __asan_unpoison_stack_memory(char *addr, size_t size) { __asan_unpoison(addr, size); } -void __asan_alloca_poison(uintptr_t addr, uintptr_t size) { +void __asan_alloca_poison(char *addr, uintptr_t size) { __asan_poison(addr - 32, 32, kAsanAllocaUnderrun); __asan_poison(addr + size, 32, kAsanAllocaOverrun); } @@ -1255,8 +1254,8 @@ void *__asan_get_current_fake_stack(void) { return 0; } -void __sanitizer_annotate_contiguous_container(long beg, long end, long old_mid, - long new_mid) { +void __sanitizer_annotate_contiguous_container(char *beg, char *end, + char *old_mid, char *new_mid) { // the c++ stl uses this // TODO(jart): make me faster __asan_unpoison(beg, new_mid - beg); @@ -1317,11 +1316,11 @@ void __asan_map_shadow(uintptr_t p, size_t n) { __repstosb((void *)(intptr_t)((int64_t)((uint64_t)a << 32) >> 16), kAsanUnmapped, size); } - __asan_unpoison((uintptr_t)p, n); + __asan_unpoison((char *)p, n); } static textstartup void __asan_shadow_string(char *s) { - __asan_map_shadow((uintptr_t)s, __strlen(s) + 1); + __asan_map_shadow((intptr_t)s, __strlen(s) + 1); } static textstartup void __asan_shadow_auxv(intptr_t *auxv) { @@ -1359,7 +1358,7 @@ static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m, static textstartup void __asan_shadow_existing_mappings(void) { __asan_shadow_mapping(&_mmi, 0); - __asan_map_shadow(GetStackAddr(0), GetStackSize()); + __asan_map_shadow((intptr_t)GetStackAddr(0), GetStackSize()); __asan_poison(GetStackAddr(0), PAGESIZE, kAsanStackOverflow); } diff --git a/libc/intrin/asan.internal.h b/libc/intrin/asan.internal.h index d74411c85..5dc92372f 100644 --- a/libc/intrin/asan.internal.h +++ b/libc/intrin/asan.internal.h @@ -16,8 +16,8 @@ struct AsanFault { const signed char *shadow; }; -void __asan_unpoison(long, long); -void __asan_poison(long, long, signed char); +void __asan_unpoison(void *, long); +void __asan_poison(void *, long, signed char); void __asan_verify(const void *, size_t); void __asan_map_shadow(uintptr_t, size_t); bool __asan_is_valid(const void *, long) nosideeffect; diff --git a/libc/intrin/assertfail.greg.c b/libc/intrin/assertfail.greg.c index 0579ff244..ceaa381db 100644 --- a/libc/intrin/assertfail.greg.c +++ b/libc/intrin/assertfail.greg.c @@ -33,8 +33,8 @@ relegated wontreturn void __assert_fail(const char *expr, const char *file, int line) { int rc; static bool noreentry; - __strace = 0; - g_ftrace = 0; + --__strace; + --__ftrace; kprintf("%s:%d: assert(%s) failed\n", file, line, expr); if (_lockcmpxchg(&noreentry, false, true)) { if (weaken(__die)) { diff --git a/libc/intrin/atomic_load.c b/libc/intrin/atomic_load.c deleted file mode 100644 index a384ce053..000000000 --- a/libc/intrin/atomic_load.c +++ /dev/null @@ -1,53 +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/intrin/atomic_load.h" - -/** - * Reads scalar from memory w/ one operation. - * - * This macro is intended to prevent things like compiler load tearing - * optimizations. - * - * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @return *(MEM) - * @note defeats compiler load tearing optimizations - * @note alignas(𝑘) is implied if compiler knows type - * @note alignas(𝑘) only avoids multi-core / cross-page edge cases - * @see Intel's Six-Thousand Page Manual V.3A §8.2.3.1 - * @see atomic_store() - */ -intptr_t(atomic_load)(void *p, size_t n) { - intptr_t x = 0; - switch (n) { - case 1: - __builtin_memcpy(&x, p, 1); - return x; - case 2: - __builtin_memcpy(&x, p, 2); - return x; - case 4: - __builtin_memcpy(&x, p, 4); - return x; - case 8: - __builtin_memcpy(&x, p, 8); - return x; - default: - return 0; - } -} diff --git a/libc/intrin/atomic_load.h b/libc/intrin/atomic_load.h deleted file mode 100644 index d5a292de9..000000000 --- a/libc/intrin/atomic_load.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ -#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -intptr_t atomic_load(void *, size_t); - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define atomic_load(MEM) \ - ({ \ - autotype(MEM) Mem = (MEM); \ - typeof(*Mem) Reg; \ - asm("mov\t%1,%0" : "=r"(Reg) : "m"(*Mem)); \ - Reg; \ - }) -#else -#define atomic_load(MEM) atomic_load(MEM, sizeof(*(MEM))) -#endif /* GNUC && !ANSI && x86 */ - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ */ diff --git a/libc/intrin/atomic_store.c b/libc/intrin/atomic_store.c deleted file mode 100644 index c4c32e6b7..000000000 --- a/libc/intrin/atomic_store.c +++ /dev/null @@ -1,54 +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/intrin/atomic_store.h" - -/** - * Saves scalar to memory w/ one operation. - * - * This is guaranteed to happen in either one or zero operations, - * depending on whether or not it's possible for *(MEM) to be read - * afterwards. This macro only forbids compiler from using >1 ops. - * - * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @param VAL is uint𝑘_t w/ better encoding for immediates (constexpr) - * @return VAL - * @note alignas(𝑘) on nexgen32e only needed for end of page gotcha - * @note alignas(𝑘) is implied if compiler knows type - * @note needed to defeat store tearing optimizations - * @see Intel Six-Thousand Page Manual Manual V.3A §8.2.3.1 - * @see atomic_load() - */ -intptr_t(atomic_store)(void *p, intptr_t x, size_t n) { - switch (n) { - case 1: - __builtin_memcpy(p, &x, 1); - return x; - case 2: - __builtin_memcpy(p, &x, 2); - return x; - case 4: - __builtin_memcpy(p, &x, 4); - return x; - case 8: - __builtin_memcpy(p, &x, 8); - return x; - default: - return 0; - } -} diff --git a/libc/intrin/atomic_store.h b/libc/intrin/atomic_store.h deleted file mode 100644 index 9a83c407c..000000000 --- a/libc/intrin/atomic_store.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ -#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -intptr_t atomic_store(void *, intptr_t, size_t); - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define atomic_store(MEM, VAL) \ - ({ \ - autotype(VAL) Val = (VAL); \ - typeof(&Val) Mem = (MEM); \ - asm("mov%z1\t%1,%0" : "=m"(*Mem) : "r"(Val)); \ - Val; \ - }) -#else -#define atomic_store(MEM, VAL) \ - atomic_store(MEM, VAL, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAL)))) -#endif /* __GNUC__ && !__STRICT_ANSI__ */ - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ */ diff --git a/libc/intrin/closehandle.greg.c b/libc/intrin/closehandle.c similarity index 100% rename from libc/intrin/closehandle.greg.c rename to libc/intrin/closehandle.c diff --git a/libc/intrin/createdirectory.greg.c b/libc/intrin/createdirectory.c similarity index 100% rename from libc/intrin/createdirectory.greg.c rename to libc/intrin/createdirectory.c diff --git a/libc/intrin/createfile.greg.c b/libc/intrin/createfile.c similarity index 100% rename from libc/intrin/createfile.greg.c rename to libc/intrin/createfile.c diff --git a/libc/intrin/createfilemapping.greg.c b/libc/intrin/createfilemapping.c similarity index 100% rename from libc/intrin/createfilemapping.greg.c rename to libc/intrin/createfilemapping.c diff --git a/libc/intrin/createfilemappingnuma.greg.c b/libc/intrin/createfilemappingnuma.c similarity index 100% rename from libc/intrin/createfilemappingnuma.greg.c rename to libc/intrin/createfilemappingnuma.c diff --git a/libc/intrin/createnamedpipe.greg.c b/libc/intrin/createnamedpipe.c similarity index 100% rename from libc/intrin/createnamedpipe.greg.c rename to libc/intrin/createnamedpipe.c diff --git a/libc/intrin/createpipe.greg.c b/libc/intrin/createpipe.c similarity index 100% rename from libc/intrin/createpipe.greg.c rename to libc/intrin/createpipe.c diff --git a/libc/intrin/createprocess.greg.c b/libc/intrin/createprocess.c similarity index 100% rename from libc/intrin/createprocess.greg.c rename to libc/intrin/createprocess.c diff --git a/libc/intrin/createsymboliclink.greg.c b/libc/intrin/createsymboliclink.c similarity index 100% rename from libc/intrin/createsymboliclink.greg.c rename to libc/intrin/createsymboliclink.c diff --git a/libc/intrin/createthread.greg.c b/libc/intrin/createthread.c similarity index 100% rename from libc/intrin/createthread.greg.c rename to libc/intrin/createthread.c diff --git a/libc/intrin/deletefile.greg.c b/libc/intrin/deletefile.c similarity index 100% rename from libc/intrin/deletefile.greg.c rename to libc/intrin/deletefile.c diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 2c822ac05..189dc829f 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -3,6 +3,7 @@ #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigaltstack.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timespec.h" @@ -27,6 +28,7 @@ const char *DescribePollFlags(char *, size_t, int); const char *DescribeStat(int, const struct stat *); const char *DescribeDirfd(char[hasatleast 12], int); const char *DescribeSigaction(char *, size_t, int, const struct sigaction *); +const char *DescribeSigaltstk(char *, size_t, int, const struct sigaltstack *); const char *DescribeSigset(char *, size_t, int, const sigset_t *); const char *DescribeRlimit(char *, size_t, int, const struct rlimit *); const char *DescribeTimespec(char *, size_t, int, const struct timespec *); diff --git a/libc/intrin/describeframe.c b/libc/intrin/describeframe.c index 1dc78593f..f252ff429 100644 --- a/libc/intrin/describeframe.c +++ b/libc/intrin/describeframe.c @@ -30,17 +30,17 @@ noasan const char *DescribeFrame(int x) { char *p; static char buf[32]; if (IsShadowFrame(x)) { - ksnprintf(buf, sizeof(buf), " /*shadow:%.12p*/", UNSHADOW(ADDR(x))); + ksnprintf(buf, sizeof(buf), " shadow=%.8x", FRAME(UNSHADOW(ADDR(x)))); return buf; - return " /*shadow*/ "; + return " shadow "; } else if (IsAutoFrame(x)) { - return " /*automap*/"; + return " automap"; } else if (IsFixedFrame(x)) { - return " /*fixed*/ "; + return " fixed "; } else if (IsArenaFrame(x)) { - return " /*arena*/ "; + return " arena "; } else if (IsStaticStackFrame(x)) { - return " /*stack*/ "; + return " stack "; } else { return ""; } diff --git a/libc/intrin/describemapflags.greg.c b/libc/intrin/describemapflags.greg.c index f30c85fe9..b0fdefd50 100644 --- a/libc/intrin/describemapflags.greg.c +++ b/libc/intrin/describemapflags.greg.c @@ -26,8 +26,8 @@ const char *DescribeMapFlags(int x) { _Alignas(char) static char mapflags[256]; const struct DescribeFlags kMapFlags[] = { {MAP_STACK, "STACK"}, // order matters - {MAP_ANONYMOUS, "ANONYMOUS"}, // {MAP_PRIVATE, "PRIVATE"}, // + {MAP_ANONYMOUS, "ANONYMOUS"}, // {MAP_SHARED, "SHARED"}, // {MAP_FIXED, "FIXED"}, // {MAP_FIXED_NOREPLACE, "FIXED_NOREPLACE"}, // diff --git a/libc/intrin/findclose.greg.c b/libc/intrin/findclose.c similarity index 100% rename from libc/intrin/findclose.greg.c rename to libc/intrin/findclose.c diff --git a/libc/intrin/findfirstfile.greg.c b/libc/intrin/findfirstfile.c similarity index 100% rename from libc/intrin/findfirstfile.greg.c rename to libc/intrin/findfirstfile.c diff --git a/libc/intrin/findnextfile.greg.c b/libc/intrin/findnextfile.c similarity index 100% rename from libc/intrin/findnextfile.greg.c rename to libc/intrin/findnextfile.c diff --git a/libc/intrin/flushfilebuffers.greg.c b/libc/intrin/flushfilebuffers.c similarity index 100% rename from libc/intrin/flushfilebuffers.greg.c rename to libc/intrin/flushfilebuffers.c diff --git a/libc/intrin/flushviewoffile.greg.c b/libc/intrin/flushviewoffile.c similarity index 100% rename from libc/intrin/flushviewoffile.greg.c rename to libc/intrin/flushviewoffile.c diff --git a/libc/intrin/ftrace.c b/libc/intrin/ftrace.c index 58f88f986..202fe746c 100644 --- a/libc/intrin/ftrace.c +++ b/libc/intrin/ftrace.c @@ -18,4 +18,25 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/runtime.h" -int g_ftrace; +/** + * Function tracing enabled state. + * + * After ftrace_install() has been called, the logging of C function + * calls may be controlled by changing this variable. If `__ftrace` is + * greater than zero, functions are logged. Otherwise, they aren't. + * + * By convention, functions wishing to disable function tracing for a + * short time period should say: + * + * void foo() { + * --__ftrace; + * bar(); + * ++__ftrace; + * } + * + * This way you still have some flexibility to force function tracing, + * by setting `__ftrace` to a higher number like `2` or `200`. Even + * though under normal circumstances, `__ftrace` should only be either + * zero or one. + */ +_Atomic(int) __ftrace; diff --git a/libc/intrin/generateconsolectrlevent.greg.c b/libc/intrin/generateconsolectrlevent.c similarity index 100% rename from libc/intrin/generateconsolectrlevent.greg.c rename to libc/intrin/generateconsolectrlevent.c diff --git a/libc/intrin/getexitcodeprocess.greg.c b/libc/intrin/getexitcodeprocess.c similarity index 100% rename from libc/intrin/getexitcodeprocess.greg.c rename to libc/intrin/getexitcodeprocess.c diff --git a/libc/intrin/getfileattributes.greg.c b/libc/intrin/getfileattributes.c similarity index 100% rename from libc/intrin/getfileattributes.greg.c rename to libc/intrin/getfileattributes.c diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index b3f880840..f3f671ea5 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -66,50 +66,57 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \ o/$(MODE)/libc/intrin/tls.greg.o \ o/$(MODE)/libc/intrin/exit.greg.o \ -o/$(MODE)/libc/intrin/errno.greg.o \ o/$(MODE)/libc/intrin/exit1.greg.o \ o/$(MODE)/libc/intrin/gettid.greg.o \ o/$(MODE)/libc/intrin/getenv.greg.o \ -o/$(MODE)/libc/intrin/createfile.greg.o \ o/$(MODE)/libc/intrin/assertfail.greg.o \ -o/$(MODE)/libc/intrin/reopenfile.greg.o \ -o/$(MODE)/libc/intrin/deletefile.greg.o \ -o/$(MODE)/libc/intrin/createpipe.greg.o \ -o/$(MODE)/libc/intrin/closehandle.greg.o \ o/$(MODE)/libc/intrin/describeiov.greg.o \ -o/$(MODE)/libc/intrin/openprocess.greg.o \ -o/$(MODE)/libc/intrin/createthread.greg.o \ o/$(MODE)/libc/intrin/describestat.greg.o \ -o/$(MODE)/libc/intrin/findnextfile.greg.o \ -o/$(MODE)/libc/intrin/createprocess.greg.o \ -o/$(MODE)/libc/intrin/findfirstfile.greg.o \ o/$(MODE)/libc/intrin/describeflags.greg.o \ o/$(MODE)/libc/intrin/describerlimit.greg.o \ -o/$(MODE)/libc/intrin/removedirectory.greg.o \ -o/$(MODE)/libc/intrin/createnamedpipe.greg.o \ -o/$(MODE)/libc/intrin/unmapviewoffile.greg.o \ -o/$(MODE)/libc/intrin/flushviewoffile.greg.o \ o/$(MODE)/libc/intrin/deviceiocontrol.greg.o \ -o/$(MODE)/libc/intrin/createdirectory.greg.o \ -o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \ -o/$(MODE)/libc/intrin/terminateprocess.greg.o \ o/$(MODE)/libc/intrin/describemapflags.greg.o \ o/$(MODE)/libc/intrin/describetimespec.greg.o \ -o/$(MODE)/libc/intrin/getfileattributes.greg.o \ -o/$(MODE)/libc/intrin/getexitcodeprocess.greg.o \ -o/$(MODE)/libc/intrin/waitforsingleobject.greg.o \ -o/$(MODE)/libc/intrin/setcurrentdirectory.greg.o \ -o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \ -o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \ -o/$(MODE)/libc/intrin/waitformultipleobjects.greg.o \ -o/$(MODE)/libc/intrin/generateconsolectrlevent.greg.o \ +o/$(MODE)/libc/intrin/createfile.o \ +o/$(MODE)/libc/intrin/reopenfile.o \ +o/$(MODE)/libc/intrin/deletefile.o \ +o/$(MODE)/libc/intrin/createpipe.o \ +o/$(MODE)/libc/intrin/closehandle.o \ +o/$(MODE)/libc/intrin/openprocess.o \ +o/$(MODE)/libc/intrin/createthread.o \ +o/$(MODE)/libc/intrin/findclose.o \ +o/$(MODE)/libc/intrin/findnextfile.o \ +o/$(MODE)/libc/intrin/createprocess.o \ +o/$(MODE)/libc/intrin/findfirstfile.o \ +o/$(MODE)/libc/intrin/removedirectory.o \ +o/$(MODE)/libc/intrin/createsymboliclink.o \ +o/$(MODE)/libc/intrin/createnamedpipe.o \ +o/$(MODE)/libc/intrin/unmapviewoffile.o \ +o/$(MODE)/libc/intrin/virtualprotect.o \ +o/$(MODE)/libc/intrin/flushviewoffile.o \ +o/$(MODE)/libc/intrin/createdirectory.o \ +o/$(MODE)/libc/intrin/flushfilebuffers.o \ +o/$(MODE)/libc/intrin/terminateprocess.o \ +o/$(MODE)/libc/intrin/getfileattributes.o \ +o/$(MODE)/libc/intrin/getexitcodeprocess.o \ +o/$(MODE)/libc/intrin/waitforsingleobject.o \ +o/$(MODE)/libc/intrin/setcurrentdirectory.o \ +o/$(MODE)/libc/intrin/mapviewoffileex.o \ +o/$(MODE)/libc/intrin/movefileex.o \ +o/$(MODE)/libc/intrin/mapviewoffileexnuma.o \ +o/$(MODE)/libc/intrin/createfilemapping.o \ +o/$(MODE)/libc/intrin/createfilemappingnuma.o \ +o/$(MODE)/libc/intrin/waitformultipleobjects.o \ +o/$(MODE)/libc/intrin/generateconsolectrlevent.o \ o/$(MODE)/libc/intrin/kstarttsc.o \ o/$(MODE)/libc/intrin/nomultics.o \ o/$(MODE)/libc/intrin/ntconsolemode.o: \ OVERRIDE_CFLAGS += \ -Os \ + -fwrapv \ -ffreestanding \ - $(NO_MAGIC) + -fno-stack-protector \ + -fno-sanitize=all o/$(MODE)/libc/intrin/describeopenflags.greg.o: \ OVERRIDE_CPPFLAGS += \ diff --git a/libc/intrin/kopenflags.S b/libc/intrin/kopenflags.S index a0749a5af..e00ebf792 100644 --- a/libc/intrin/kopenflags.S +++ b/libc/intrin/kopenflags.S @@ -40,6 +40,7 @@ kOpenFlags: .e O_TRUNC,"TRUNC" // .e O_CLOEXEC,"CLOEXEC" // .e O_NONBLOCK,"NONBLOCK" // + .e O_DIRECTORY,"DIRECTORY" // .e O_DIRECT,"DIRECT" // no-op on xnu/openbsd .e O_APPEND,"APPEND" // weird on nt .e O_TMPFILE,"TMPFILE" // linux, windows diff --git a/libc/intrin/mapviewoffileex.greg.c b/libc/intrin/mapviewoffileex.c similarity index 100% rename from libc/intrin/mapviewoffileex.greg.c rename to libc/intrin/mapviewoffileex.c diff --git a/libc/intrin/mapviewoffileexnuma.greg.c b/libc/intrin/mapviewoffileexnuma.c similarity index 100% rename from libc/intrin/mapviewoffileexnuma.greg.c rename to libc/intrin/mapviewoffileexnuma.c diff --git a/libc/intrin/movefileex.greg.c b/libc/intrin/movefileex.c similarity index 100% rename from libc/intrin/movefileex.greg.c rename to libc/intrin/movefileex.c diff --git a/libc/intrin/openprocess.greg.c b/libc/intrin/openprocess.c similarity index 100% rename from libc/intrin/openprocess.greg.c rename to libc/intrin/openprocess.c diff --git a/libc/intrin/removedirectory.greg.c b/libc/intrin/removedirectory.c similarity index 100% rename from libc/intrin/removedirectory.greg.c rename to libc/intrin/removedirectory.c diff --git a/libc/intrin/reopenfile.greg.c b/libc/intrin/reopenfile.c similarity index 100% rename from libc/intrin/reopenfile.greg.c rename to libc/intrin/reopenfile.c diff --git a/libc/intrin/sched_yield.S b/libc/intrin/sched_yield.S new file mode 100644 index 000000000..9f6059da9 --- /dev/null +++ b/libc/intrin/sched_yield.S @@ -0,0 +1,54 @@ +/*-*- 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/dce.h" +#include "libc/sysv/consts/nr.h" +#include "libc/macros.internal.h" + +// Asks kernel to let other threads be scheduled. +// +// @return 0 on success, or -1 w/ errno +sched_yield: + push %rbp + mov %rsp,%rbp + testb IsWindows() + jnz 1f + +// UNIX Support + mov __NR_sched_yield,%eax + syscall + jmp 2f + +// Windows Support +// +// A value of zero, together with the bAlertable parameter set to +// FALSE, causes the thread to relinquish the remainder of its time +// slice to any other thread that is ready to run, if there are no +// pending user APCs on the calling thread. If there are no other +// threads ready to run and no user APCs are queued, the function +// returns immediately, and the thread continues execution. +// ──Quoth MSDN +1: xor %ecx,%ecx + xor %edx,%edx + ntcall __imp_SleepEx + xor %eax,%eax + +2: pop %rbp + ret + .endfn sched_yield,globl + .previous diff --git a/libc/intrin/setcurrentdirectory.greg.c b/libc/intrin/setcurrentdirectory.c similarity index 100% rename from libc/intrin/setcurrentdirectory.greg.c rename to libc/intrin/setcurrentdirectory.c diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index e85051b36..c58c72f23 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -1,13 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ #define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ -#include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchg.h" -#include "libc/log/backtrace.internal.h" -#include "libc/log/log.h" -#include "libc/runtime/symbols.internal.h" #if IsModeDbg() && !defined(_SPINLOCK_DEBUG) #define _SPINLOCK_DEBUG @@ -15,13 +10,13 @@ #if defined(_SPINLOCK_DEBUG) #define _spinlock(lock) _spinlock_ndebug(lock) -#define _spinlock_ndebug(lock) _spinlock_optimistic(lock) +#define _spinlock_ndebug(lock) _spinlock_cooperative(lock) #elif defined(TINY) #define _spinlock(lock) _spinlock_tiny(lock) #define _spinlock_ndebug(lock) _spinlock_tiny(lock) #else -#define _spinlock(lock) _spinlock_optimistic(lock) -#define _spinlock_ndebug(lock) _spinlock_optimistic(lock) +#define _spinlock(lock) _spinlock_cooperative(lock) +#define _spinlock_ndebug(lock) _spinlock_cooperative(lock) #endif #define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED) @@ -41,15 +36,18 @@ } \ } while (0) -#define _spinlock_optimistic(lock) \ +#define _spinlock_cooperative(lock) \ do { \ + int __tries = 0; \ for (;;) { \ typeof(*(lock)) x; \ __atomic_load(lock, &x, __ATOMIC_RELAXED); \ if (!x && !_trylock(lock)) { \ break; \ - } else { \ + } else if (++__tries & 7) { \ __builtin_ia32_pause(); \ + } else { \ + sched_yield(); \ } \ } \ } while (0) @@ -57,7 +55,7 @@ #define _spinlock_debug(lock) \ do { \ typeof(*(lock)) me, owner; \ - unsigned long warntries = 10000000; \ + unsigned long warntries = 16777216; \ me = gettid(); \ if (!_lockcmpxchg(lock, 0, me)) { \ __atomic_load(lock, &owner, __ATOMIC_RELAXED); \ @@ -71,7 +69,11 @@ kprintf("%s:%d: warning: possible deadlock on %s in %s()\n", \ __FILE__, __LINE__, #lock, __FUNCTION__); \ } \ - __builtin_ia32_pause(); \ + if (warntries & 7) { \ + __builtin_ia32_pause(); \ + } else { \ + sched_yield(); \ + } \ } \ } \ } while (0) diff --git a/libc/intrin/stracef.greg.c b/libc/intrin/stracef.greg.c index ab5fbc4fe..6ca880dbf 100644 --- a/libc/intrin/stracef.greg.c +++ b/libc/intrin/stracef.greg.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/strace.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/runtime/runtime.h" privileged void __stracef(const char *fmt, ...) { va_list v; diff --git a/libc/intrin/terminateprocess.greg.c b/libc/intrin/terminateprocess.c similarity index 100% rename from libc/intrin/terminateprocess.greg.c rename to libc/intrin/terminateprocess.c diff --git a/libc/intrin/unmapviewoffile.greg.c b/libc/intrin/unmapviewoffile.c similarity index 100% rename from libc/intrin/unmapviewoffile.greg.c rename to libc/intrin/unmapviewoffile.c diff --git a/libc/intrin/virtualprotect.greg.c b/libc/intrin/virtualprotect.c similarity index 100% rename from libc/intrin/virtualprotect.greg.c rename to libc/intrin/virtualprotect.c diff --git a/libc/intrin/waitformultipleobjects.greg.c b/libc/intrin/waitformultipleobjects.c similarity index 100% rename from libc/intrin/waitformultipleobjects.greg.c rename to libc/intrin/waitformultipleobjects.c diff --git a/libc/intrin/waitforsingleobject.greg.c b/libc/intrin/waitforsingleobject.c similarity index 100% rename from libc/intrin/waitforsingleobject.greg.c rename to libc/intrin/waitforsingleobject.c diff --git a/libc/isystem/stdatomic.h b/libc/isystem/stdatomic.h new file mode 100644 index 000000000..319a98beb --- /dev/null +++ b/libc/isystem/stdatomic.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_STDATOMIC_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_STDATOMIC_H_ +#include "libc/bits/atomic.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_STDATOMIC_H_ */ diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 05aee9fa4..8512da804 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -21,6 +21,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" @@ -86,6 +87,11 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { return -1; } + if (IsLinux() && !__is_linux_2_6_23()) { + // we need the `addr2line -a` option + return -1; + } + i = 0; j = 0; argv[i++] = "addr2line"; @@ -177,12 +183,12 @@ static int PrintBacktrace(int fd, const struct StackFrame *bp) { void ShowBacktrace(int fd, const struct StackFrame *bp) { #ifdef __FNO_OMIT_FRAME_POINTER__ /* asan runtime depends on this function */ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); - __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); + --__ftrace; + --__strace; if (!bp) bp = __builtin_frame_address(0); PrintBacktrace(fd, bp); - __atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); + ++__strace; + ++__ftrace; #else (fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n" "\t-D__FNO_OMIT_FRAME_POINTER__\n" diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index d98055e7e..ba43b5123 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -45,8 +45,8 @@ relegated void __check_fail(const char *suffix, const char *opstr, size_t i; va_list va; char hostname[32]; - __strace = 0; - g_ftrace = 0; + --__strace; + --__ftrace; e = errno; __start_fatal(file, line); __stpcpy(hostname, "unknown"); diff --git a/libc/log/log.h b/libc/log/log.h index 1c9c8b93b..18be04844 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -78,15 +78,15 @@ extern unsigned __log_level; /* log level for runtime check */ // log a message with the specified log level (not checking if LOGGABLE) #define LOGF(LEVEL, FMT, ...) \ do { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } while (0) // die with an error message without backtrace and debugger invocation #define DIEF(FMT, ...) \ do { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ if (weaken(__die)) weaken(__die)(); \ exit(1); \ @@ -95,7 +95,7 @@ extern unsigned __log_level; /* log level for runtime check */ #define FATALF(FMT, ...) \ do { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -103,78 +103,78 @@ extern unsigned __log_level; /* log level for runtime check */ #define ERRORF(FMT, ...) \ do { \ if (LOGGABLE(kLogError)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define WARNF(FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define INFOF(FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define VERBOSEF(FMT, ...) \ do { \ if (LOGGABLE(kLogVerbose)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define DEBUGF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define NOISEF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define FLOGF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define FWARNF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define FFATALF(F, FMT, ...) \ do { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -182,18 +182,18 @@ extern unsigned __log_level; /* log level for runtime check */ #define FDEBUGF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) #define FNOISEF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ } \ } while (0) @@ -206,25 +206,25 @@ extern unsigned __log_level; /* log level for runtime check */ int e = errno; \ autotype(FORM) Ax = (FORM); \ if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ + --__ftrace; \ __logerrno(__FILE__, __LINE__, #FORM); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ + ++__ftrace; \ errno = e; \ } \ Ax; \ }) -#define LOGIFNULL(FORM) \ - ({ \ - int e = errno; \ - autotype(FORM) Ax = (FORM); \ - if (Ax == NULL && LOGGABLE(kLogWarn)) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ - __logerrno(__FILE__, __LINE__, #FORM); \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ - errno = e; \ - } \ - Ax; \ +#define LOGIFNULL(FORM) \ + ({ \ + int e = errno; \ + autotype(FORM) Ax = (FORM); \ + if (Ax == NULL && LOGGABLE(kLogWarn)) { \ + --__ftrace; \ + __logerrno(__FILE__, __LINE__, #FORM); \ + ++__ftrace; \ + errno = e; \ + } \ + Ax; \ }) /*───────────────────────────────────────────────────────────────────────────│─╗ diff --git a/libc/log/log.mk b/libc/log/log.mk index 593dd3982..36930a76f 100644 --- a/libc/log/log.mk +++ b/libc/log/log.mk @@ -75,7 +75,6 @@ o/$(MODE)/libc/log/backtrace3.o \ o/$(MODE)/libc/log/checkaligned.o \ o/$(MODE)/libc/log/checkfail.o \ o/$(MODE)/libc/log/checkfail_ndebug.o \ -o/$(MODE)/libc/log/getsymboltable.o \ o/$(MODE)/libc/log/restoretty.o \ o/$(MODE)/libc/log/oncrash.o \ o/$(MODE)/libc/log/onkill.o \ diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index dce6c0fb2..325d0273a 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -206,8 +206,9 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, " %m\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) + PAGESIZE)) + (ctx && + (ctx->uc_mcontext.rsp >= (intptr_t)GetStaticStackAddr(0) && + ctx->uc_mcontext.rsp <= (intptr_t)GetStaticStackAddr(0) + PAGESIZE)) ? "Stack Overflow" : GetSiCodeName(sig, si->si_code), host, getpid(), gettid(), program_invocation_name, names.sysname, @@ -278,8 +279,8 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, int gdbpid, err; static bool noreentry, notpossible; STRACE("__oncrash rip %x", ctx->uc_mcontext.rip); - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); - __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); + --__ftrace; + --__strace; if (_lockcmpxchg(&noreentry, false, true)) { if (!__vforked) { rip = ctx ? ctx->uc_mcontext.rip : 0; @@ -317,6 +318,6 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, } noreentry = false; ItsATrap: - __atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); + ++__strace; + ++__ftrace; } diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 66edf6490..86efb79ad 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -31,8 +31,14 @@ STATIC_YOINK("__die"); /* for backtracing */ STATIC_YOINK("malloc_inspect_all"); /* for asan memory origin */ STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */ +static struct sigaltstack oldsigaltstack; extern const unsigned char __oncrash_thunks[8][11]; +static void FreeSigAltStack(void *p) { + sigaltstack(&oldsigaltstack, 0); + free(p); +} + /** * Installs crash signal handlers. * @@ -63,17 +69,24 @@ void ShowCrashReports(void) { kCrashSigs[5] = SIGABRT; /* abort() called */ kCrashSigs[6] = SIGBUS; /* misaligned, noncanonical ptr, etc. */ /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ + if (!IsWindows()) { + bzero(&ss, sizeof(ss)); + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if ((ss.ss_sp = malloc(SIGSTKSZ))) { + if (!sigaltstack(&ss, &oldsigaltstack)) { + __cxa_atexit(FreeSigAltStack, ss.ss_sp, 0); + } else { + free(ss.ss_sp); + } + } + } bzero(&sa, sizeof(sa)); - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - ss.ss_sp = malloc(SIGSTKSZ); - __cxa_atexit(free, ss.ss_sp, 0); sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; sigfillset(&sa.sa_mask); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { sigdelset(&sa.sa_mask, kCrashSigs[i]); } - if (!IsWindows()) sigaltstack(&ss, 0); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { if (kCrashSigs[i]) { sa.sa_sigaction = (sigaction_f)__oncrash_thunks[i]; diff --git a/libc/mem/putenv.c b/libc/mem/putenv.c index 27ff77240..be407b1b9 100644 --- a/libc/mem/putenv.c +++ b/libc/mem/putenv.c @@ -105,10 +105,14 @@ Fail: return einval(); } +static void UnsetenvFree(void *p) { + free(p); +} + /* weakly called by unsetenv() when removing a pointer */ void __freeenv(void *p) { if (once) { - __cxa_atexit(free, p, 0); + __cxa_atexit(UnsetenvFree, p, 0); } } diff --git a/libc/runtime/arememoryintervalsok.c b/libc/runtime/arememoryintervalsok.c index 1a4969a14..5b7d4fe9c 100644 --- a/libc/runtime/arememoryintervalsok.c +++ b/libc/runtime/arememoryintervalsok.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/strace.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/memtrack.internal.h" noasan bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) { @@ -27,6 +28,12 @@ noasan bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) { STRACE("AreMemoryIntervalsOk() y should be >= x!"); return false; } + if (!(mm->p[i].size <= + (size_t)(mm->p[i].y - mm->p[i].x) * FRAMESIZE + FRAMESIZE && + mm->p[i].size > (size_t)(mm->p[i].y - mm->p[i].x) * FRAMESIZE)) { + STRACE("AreMemoryIntervalsOk() size is wrong!"); + return false; + } if (i) { if (mm->p[i].h != -1 || mm->p[i - 1].h != -1) { if (mm->p[i].x <= mm->p[i - 1].y) { diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index e737f6f3e..39b464562 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -45,7 +45,6 @@ #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" -#include "libc/sock/ntstdin.internal.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" @@ -222,7 +221,6 @@ textwindows void WinMainForked(void) { // rewrap the stdin named pipe hack // since the handles closed on fork - if (weaken(ForkNtStdinWorker)) weaken(ForkNtStdinWorker)(); struct Fds *fds = VEIL("r", &g_fds); fds->p[0].handle = fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); diff --git a/libc/runtime/ftrace-hook.S b/libc/runtime/ftrace-hook.S index a679c52b1..b4d7f2885 100644 --- a/libc/runtime/ftrace-hook.S +++ b/libc/runtime/ftrace-hook.S @@ -20,7 +20,7 @@ .privileged ftrace_hook: - cmp $0,g_ftrace(%rip) + cmp $0,__ftrace(%rip) jg 1f ret 1: push %rbp diff --git a/libc/runtime/ftraceinit.greg.c b/libc/runtime/ftraceinit.greg.c index 48bbc05aa..cb212bda1 100644 --- a/libc/runtime/ftraceinit.greg.c +++ b/libc/runtime/ftraceinit.greg.c @@ -35,7 +35,7 @@ textstartup int ftrace_init(void) { if (__intercept_flag(&__argc, __argv, "--ftrace")) { ftrace_install(); - ++g_ftrace; + ++__ftrace; } return __argc; } diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index d3a63c7c0..c3494741a 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -17,24 +17,26 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" +#include "libc/math.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtscp.h" #include "libc/nexgen32e/stackframe.h" #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/time/clockstonanos.internal.h" -#pragma weak stderr - #define MAX_NESTING 512 /** @@ -47,13 +49,16 @@ void ftrace_hook(void); -bool ftrace_enabled; -static int g_skew; -static int64_t g_lastaddr; -static uint64_t g_laststamp; +_Alignas(64) char ftrace_lock; -static privileged noinstrument noasan noubsan int GetNestingLevelImpl( - struct StackFrame *frame) { +static struct Ftrace { + int skew; + int stackdigs; + int64_t lastaddr; + uint64_t laststamp; +} g_ftrace; + +static privileged int GetNestingLevelImpl(struct StackFrame *frame) { int nesting = -2; while (frame) { ++nesting; @@ -62,12 +67,11 @@ static privileged noinstrument noasan noubsan int GetNestingLevelImpl( return MAX(0, nesting); } -static privileged noinstrument noasan noubsan int GetNestingLevel( - struct StackFrame *frame) { +static privileged int GetNestingLevel(struct StackFrame *frame) { int nesting; nesting = GetNestingLevelImpl(frame); - if (nesting < g_skew) g_skew = nesting; - nesting -= g_skew; + if (nesting < g_ftrace.skew) g_ftrace.skew = nesting; + nesting -= g_ftrace.skew; return MIN(MAX_NESTING, nesting); } @@ -78,32 +82,30 @@ static privileged noinstrument noasan noubsan int GetNestingLevel( * prologues of other functions. We assume those functions behave * according to the System Five NexGen32e ABI. */ -privileged noinstrument noasan noubsan void ftracer(void) { - /* asan runtime depends on this function */ +privileged void ftracer(void) { uint64_t stamp; - static bool noreentry; + size_t stackuse; struct StackFrame *frame; - if (!_cmpxchg(&noreentry, 0, 1)) return; - if (ftrace_enabled) { - stamp = rdtsc(); - frame = __builtin_frame_address(0); - frame = frame->next; - if (frame->addr != g_lastaddr) { - kprintf("%rFUN %5P %'18T %*s%t\r\n", GetNestingLevel(frame) * 2, "", - frame->addr); - g_laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); - g_lastaddr = frame->addr; - } + _spinlock_cooperative(&ftrace_lock); + stamp = rdtsc(); + frame = __builtin_frame_address(0); + frame = frame->next; + if (frame->addr != g_ftrace.lastaddr) { + stackuse = ROUNDUP((intptr_t)frame, GetStackSize()) - (intptr_t)frame; + kprintf("%rFUN %5P %'13T %'*lu %*s%t\r\n", g_ftrace.stackdigs, stackuse, + GetNestingLevel(frame) * 2, "", frame->addr); + g_ftrace.laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); + g_ftrace.lastaddr = frame->addr; } - noreentry = 0; + _spunlock(&ftrace_lock); } textstartup int ftrace_install(void) { if (GetSymbolTable()) { - g_lastaddr = -1; - g_laststamp = kStartTsc; - g_skew = GetNestingLevelImpl(__builtin_frame_address(0)); - ftrace_enabled = 1; + g_ftrace.lastaddr = -1; + g_ftrace.laststamp = kStartTsc; + g_ftrace.stackdigs = LengthInt64Thousands(GetStackSize()); + g_ftrace.skew = GetNestingLevelImpl(__builtin_frame_address(0)); return __hook(ftrace_hook, GetSymbolTable()); } else { kprintf("error: --ftrace failed to open symbol table\r\n"); diff --git a/libc/runtime/memtrack.greg.c b/libc/runtime/memtrack.greg.c index 2d69982d6..e4f58c759 100644 --- a/libc/runtime/memtrack.greg.c +++ b/libc/runtime/memtrack.greg.c @@ -26,6 +26,7 @@ #include "libc/errno.h" #include "libc/intrin/asan.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/runtime/directmap.internal.h" @@ -112,7 +113,9 @@ int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { if (CreateMemoryInterval(mm, i) == -1) return -1; - mm->p[i].y = x - 1; + mm->p[i + 0].size -= (size_t)(mm->p[i + 0].y - (x - 1)) * FRAMESIZE; + mm->p[i + 0].y = x - 1; + mm->p[i + 1].size -= (size_t)((y + 1) - mm->p[i + 1].x) * FRAMESIZE; mm->p[i + 1].x = y + 1; return 0; } @@ -123,31 +126,60 @@ int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, assert(y >= x); assert(AreMemoryIntervalsOk(mm)); if (!mm->i) return 0; + + // binary search for the lefthand side l = FindMemoryInterval(mm, x); if (l == mm->i) return 0; - if (!l && y < mm->p[l].x) return 0; if (y < mm->p[l].x) return 0; + + // binary search for the righthand side r = FindMemoryInterval(mm, y); if (r == mm->i || (r > l && y < mm->p[r].x)) --r; assert(r >= l); assert(x <= mm->p[r].y); + + // remove the middle of an existing map + // + // ----|mmmmmmmmmmmmmmmm|--------- before + // xxxxx + // ----|mmmm|-----|mmmmm|--------- after + // + // this isn't possible on windows because we track each + // 64kb segment on that platform using a separate entry if (l == r && x > mm->p[l].x && y < mm->p[l].y) { return PunchHole(mm, x, y, l); } + + // trim the right side of the lefthand map + // + // ----|mmmmmmm|-------------- before + // xxxxx + // ----|mmmm|----------------- after + // if (x > mm->p[l].x && x <= mm->p[l].y) { assert(y >= mm->p[l].y); if (IsWindows()) return einval(); + mm->p[l].size -= (size_t)(mm->p[l].y - (x - 1)) * FRAMESIZE; mm->p[l].y = x - 1; assert(mm->p[l].x <= mm->p[l].y); ++l; } + + // trim the left side of the righthand map + // + // ------------|mmmmm|-------- before + // xxxxx + // ---------------|mm|-------- after + // if (y >= mm->p[r].x && y < mm->p[r].y) { assert(x <= mm->p[r].x); if (IsWindows()) return einval(); + mm->p[r].size -= (size_t)((y + 1) - mm->p[r].x) * FRAMESIZE; mm->p[r].x = y + 1; assert(mm->p[r].x <= mm->p[r].y); --r; } + if (l <= r) { if (IsWindows() && wf) { wf(mm, l, r); @@ -164,19 +196,38 @@ int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, unsigned i; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); + i = FindMemoryInterval(mm, x); + + // try to extend the righthand side of the lefthand entry + // we can't do that if we're tracking independent handles + // we can't do that if it's a file map with a small size! if (i && x == mm->p[i - 1].y + 1 && h == mm->p[i - 1].h && - prot == mm->p[i - 1].prot && flags == mm->p[i - 1].flags) { + prot == mm->p[i - 1].prot && flags == mm->p[i - 1].flags && + mm->p[i - 1].size == + (size_t)(mm->p[i - 1].y - mm->p[i - 1].x) * FRAMESIZE + FRAMESIZE) { + mm->p[i - 1].size += (size_t)(y - mm->p[i - 1].y) * FRAMESIZE; mm->p[i - 1].y = y; + // if we filled the hole then merge the two mappings if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h && prot == mm->p[i].prot && flags == mm->p[i].flags) { mm->p[i - 1].y = mm->p[i].y; + mm->p[i - 1].size += mm->p[i].size; RemoveMemoryIntervals(mm, i, 1); } - } else if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h && - prot == mm->p[i].prot && flags == mm->p[i].flags) { + } + + // try to extend the lefthand side of the righthand entry + // we can't do that if we're creating a smaller file map! + else if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h && + prot == mm->p[i].prot && flags == mm->p[i].flags && + size == (size_t)(y - x) * FRAMESIZE + FRAMESIZE) { + mm->p[i].size += (size_t)(mm->p[i].x - x) * FRAMESIZE; mm->p[i].x = x; - } else { + } + + // otherwise, create a new entry and memmove the items + else { if (CreateMemoryInterval(mm, i) == -1) return -1; mm->p[i].x = x; mm->p[i].y = y; diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 97a38801d..72c3fb3e3 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -34,10 +34,10 @@ struct MemoryInterval { int x; int y; long h; + long size; int prot; int flags; long offset; - long size; bool iscow; bool readonlyfile; }; @@ -90,25 +90,27 @@ forceinline pureconst bool IsShadowFrame(int x) { } forceinline pureconst bool IsKernelFrame(int x) { - return (int)(GetStaticStackAddr(0) >> 16) <= x && - x <= (int)((GetStaticStackAddr(0) + (GetStackSize() - FRAMESIZE)) >> - 16); + intptr_t stack = (intptr_t)GetStaticStackAddr(0); + return (int)(stack >> 16) <= x && + x <= (int)((stack + (GetStackSize() - FRAMESIZE)) >> 16); } forceinline pureconst bool IsStaticStackFrame(int x) { - return (int)(GetStaticStackAddr(0) >> 16) <= x && - x <= (int)((GetStaticStackAddr(0) + (GetStackSize() - FRAMESIZE)) >> - 16); + intptr_t stack = (intptr_t)GetStaticStackAddr(0); + return (int)(stack >> 16) <= x && + x <= (int)((stack + (GetStackSize() - FRAMESIZE)) >> 16); } forceinline pureconst bool IsStackFrame(int x) { - return (int)(GetStackAddr(0) >> 16) <= x && - x <= (int)((GetStackAddr(0) + (GetStackSize() - FRAMESIZE)) >> 16); + intptr_t stack = (intptr_t)GetStackAddr(0); + return (int)(stack >> 16) <= x && + x <= (int)((stack + (GetStackSize() - FRAMESIZE)) >> 16); } forceinline pureconst bool IsSigAltStackFrame(int x) { - return (int)(GetStackAddr(0) >> 16) <= x && - x <= (int)((GetStackAddr(0) + (SIGSTKSZ - FRAMESIZE)) >> 16); + intptr_t stack = (intptr_t)GetStackAddr(0); + return (int)(stack >> 16) <= x && + x <= (int)((stack + (SIGSTKSZ - FRAMESIZE)) >> 16); } forceinline pureconst bool IsOldStackFrame(int x) { diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index 19146c221..51c43e546 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -16,16 +16,22 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/likely.h" +#include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" +#include "libc/log/backtrace.internal.h" #include "libc/log/libfatal.internal.h" +#include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/runtime/directmap.internal.h" +#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -36,89 +42,123 @@ #define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) #define FRAME(x) ((int)((intptr_t)(x) >> 16)) -static noasan int Munmap(void *v, size_t n) { - char poison, *p = v; +static noasan int Munmap(char *, size_t); + +static noasan void MunmapShadow(char *p, size_t n) { + intptr_t a, b, x, y; + KERNTRACE("MunmapShadow(%p, %'zu)", p, n); + a = ((intptr_t)p >> 3) + 0x7fff8000; + b = a + (n >> 3); + if (IsMemtracked(FRAME(a), FRAME(b - 1))) { + x = ROUNDUP(a, FRAMESIZE); + y = ROUNDDOWN(b, FRAMESIZE); + if (0 && x < y) { + // delete shadowspace if unmapping ≥512kb. in practice it has + // to be >1mb since we can only unmap it if it's aligned, and + // as such we poison the edges if there are any. + __repstosb((void *)a, kAsanUnmapped, x - a); + Munmap((void *)x, y - x); + __repstosb((void *)y, kAsanUnmapped, b - y); + } else { + // otherwise just poison and assume reuse + __repstosb((void *)a, kAsanUnmapped, b - a); + } + } else { + STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); + } +} + +// 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 noasan void MunmapImpl(char *p, size_t n) { + char *q; + size_t m; + intptr_t a, b, c; + int i, l, r, rc, beg, end; + KERNTRACE("MunmapImpl(%p, %'zu)", p, n); + l = FRAME(p); + r = FRAME(p + n - 1); + i = FindMemoryInterval(&_mmi, l); + for (; i < _mmi.i && r >= _mmi.p[i].x; ++i) { + if (l >= _mmi.p[i].x && r <= _mmi.p[i].y) { + + // it's contained within the entry + beg = l; + end = r; + } else if (l <= _mmi.p[i].x && r >= _mmi.p[i].x) { + + // it overlaps with the lefthand side of the entry + beg = _mmi.p[i].x; + end = MIN(r, _mmi.p[i].y); + } else if (l <= _mmi.p[i].y && r >= _mmi.p[i].y) { + + // it overlaps with the righthand side of the entry + beg = MAX(_mmi.p[i].x, l); + end = _mmi.p[i].y; + } else { + // shouldn't be possible + assert(!"binary search panic"); + continue; + } + // openbsd even requires that if we mapped, for instance a 5 byte + // file, that we be sure to call munmap(file, 5). let's abstract! + a = ADDR(beg); + b = ADDR(end) + FRAMESIZE; + c = ADDR(_mmi.p[i].x) + _mmi.p[i].size; + q = (char *)a; + m = MIN(b, c) - a; + if (!IsWindows()) { + rc = sys_munmap(q, m); + assert(!rc); + } else { + // Handled by UntrackMemoryIntervals() on Windows + } + if (IsAsan() && !OverlapsShadowSpace(p, n)) { + MunmapShadow(q, m); + } + } +} + +static noasan int Munmap(char *p, size_t n) { + unsigned i; + char poison; intptr_t a, b, x, y; assert(!__vforked); - if (UNLIKELY(!n)) { - STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n); + STRACE("munmap(%.12p, %'zu) EINVAL (n=0)", p, n); return einval(); } - if (UNLIKELY(!IsLegalSize(n))) { STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n); return einval(); } - if (UNLIKELY(!IsLegalPointer(p))) { STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n); return einval(); } - if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) { STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n); return einval(); } - if (UNLIKELY(!ALIGNED(p))) { STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n); return einval(); } - - if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { - STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n); - return efault(); - } - - if (UntrackMemoryIntervals(p, n) == -1) { - return -1; - } - - if (IsWindows()) { - return 0; // UntrackMemoryIntervals does it for NT - } - - if (sys_munmap(p, n) == -1) { - return -1; // ouch - } - - if (IsAsan() && !OverlapsShadowSpace(p, n)) { - a = ((intptr_t)p >> 3) + 0x7fff8000; - b = a + (n >> 3); - if (IsMemtracked(FRAME(a), FRAME(b - 1))) { - x = ROUNDUP(a, FRAMESIZE); - y = ROUNDDOWN(b, FRAMESIZE); - if (x < y) { - // delete shadowspace if unmapping ≥512kb - __repstosb((void *)a, kAsanUnmapped, x - a); - Munmap((void *)x, y - x); - __repstosb((void *)y, kAsanUnmapped, b - y); - } else { - // otherwise just poison and assume reuse - __repstosb((void *)a, kAsanUnmapped, b - a); - } - } else { - STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); - } - } - - return 0; + MunmapImpl(p, n); + return UntrackMemoryIntervals(p, n); } /** * Releases memory pages. * - * This function may be used to punch holes in existing mappings, but - * your mileage may vary on Windows. - * - * @param p is a pointer within any memory mapped region the process - * has permission to control, such as address ranges returned by - * mmap(), the program image itself, etc. - * @param n is the number of bytes to be unmapped, and needs to be a - * multiple of FRAMESIZE for anonymous mappings, because windows - * and for files size needs to be perfect to the byte bc openbsd + * @param p is the beginning of the memory region to unmap + * @param n is the number of bytes to be unmapped * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `n == 0` + * @raises EINVAL if `n` isn't 48-bit + * @raises EINVAL if `p+(n-1)` isn't 48-bit + * @raises EINVAL if `p` isn't 65536-byte aligned */ noasan int munmap(void *p, size_t n) { int rc; diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 3d871d8fb..168597e99 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -147,8 +147,8 @@ textstartup void __printargs(const char *prologue) { struct pollfd pfds[128]; } u; - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); - __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); + --__ftrace; + --__strace; e = errno; PRINT(""); @@ -547,7 +547,7 @@ textstartup void __printargs(const char *prologue) { } PRINT(""); - __atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); + ++__strace; + ++__ftrace; errno = e; } diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index 2fdfebce4..00a1e5825 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -41,10 +41,16 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { for (i = 0; i < mm->i; ++i) { frames = mm->p[i].y + 1 - mm->p[i].x; maptally += frames; - kprintf("%012lx-%012lx %s %'*ldx%s", ADDR(mm->p[i].x), ADDR(mm->p[i].y + 1), + kprintf("%08x-%08x %s %'*ldx%s", mm->p[i].x, mm->p[i].y, DescribeMapping(mm->p[i].prot, mm->p[i].flags, mode), w, frames, DescribeFrame(mm->p[i].x)); - if (i + 1 < _mmi.i) { + if (mm->p[i].iscow) kprintf(" cow"); + if (mm->p[i].readonlyfile) kprintf(" readonlyfile"); + if (mm->p[i].size != + (size_t)(mm->p[i].y - mm->p[i].x) * FRAMESIZE + FRAMESIZE) { + kprintf(" size=%'zu", mm->p[i].size); + } + if (i + 1 < mm->i) { frames = mm->p[i + 1].x - mm->p[i].y - 1; if (frames && IsNoteworthyHole(i, mm)) { gaptally += frames; diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index e813ad377..a5b4d50cc 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -14,10 +14,12 @@ extern char **__argv; /* CRT */ extern char **__envp; /* CRT */ extern unsigned long *__auxv; /* CRT */ extern intptr_t __oldstack; /* CRT */ +extern uint64_t __nosync; /* SYS */ +extern _Atomic(int) __ftrace; /* SYS */ +extern _Atomic(int) __strace; /* SYS */ extern char *program_invocation_name; /* RII */ extern char *program_invocation_short_name; /* RII */ -extern int g_ftrace; /* CRT */ -extern uint64_t g_syscount; /* RII */ +extern uint64_t __syscount; /* RII */ extern const uint64_t kStartTsc; /* RII */ extern const char kTmpPath[]; /* RII */ extern const char kNtSystemDirectory[]; /* RII */ @@ -38,7 +40,6 @@ extern unsigned char *__relo_start[]; /* αpε */ extern unsigned char *__relo_end[]; /* αpε */ extern uint8_t __zip_start[]; /* αpε */ extern uint8_t __zip_end[]; /* αpε */ -extern bool ftrace_enabled; extern size_t __virtualmax; extern bool __isworker; diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index d7d49bf55..63fc71036 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -58,24 +58,25 @@ $(LIBC_RUNTIME_A).pkg: \ $(LIBC_RUNTIME_A_OBJS) \ $(foreach x,$(LIBC_RUNTIME_A_DIRECTDEPS),$($(x)_A).pkg) +# we can't use asan and ubsan because: +# asan and ubsan can be function traced +# we can't use function tracing because: +# this is the function tracing runtime +o/$(MODE)/libc/runtime/ftracer.o: \ + OVERRIDE_CFLAGS += \ + -mno-fentry \ + -ffreestanding \ + -fno-sanitize=all + o/$(MODE)/libc/runtime/fork-nt.o \ o/$(MODE)/libc/runtime/printmemoryintervals.o \ o/$(MODE)/libc/runtime/arememoryintervalsok.o \ -o/$(MODE)/libc/runtime/directmap.o \ -o/$(MODE)/libc/runtime/directmapnt.o \ o/$(MODE)/libc/runtime/findmemoryinterval.o \ -o/$(MODE)/libc/runtime/ftrace.greg.o \ o/$(MODE)/libc/runtime/sys_mprotect.greg.o \ -o/$(MODE)/libc/runtime/ftracer.o \ -o/$(MODE)/libc/runtime/ezmap.o \ o/$(MODE)/libc/runtime/getdosargv.o \ o/$(MODE)/libc/runtime/getdosenviron.o \ o/$(MODE)/libc/runtime/hook.greg.o \ -o/$(MODE)/libc/runtime/morph.greg.o \ -o/$(MODE)/libc/runtime/mprotect.greg.o \ -o/$(MODE)/libc/runtime/mprotect-nt.greg.o \ o/$(MODE)/libc/runtime/ismemtracked.greg.o \ -o/$(MODE)/libc/runtime/isheap.o \ o/$(MODE)/libc/runtime/memtracknt.o \ o/$(MODE)/libc/runtime/memtrack.greg.o \ o/$(MODE)/libc/runtime/metalprintf.greg.o \ diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index 789094553..4e231d54a 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -61,8 +61,9 @@ extern char ape_stack_align[] __attribute__((__weak__)); /** * Returns address of bottom of stack. */ -#define GetStackAddr(ADDEND) \ - ((((intptr_t)__builtin_frame_address(0) - 1) & -GetStackSize()) + (ADDEND)) +#define GetStackAddr(ADDEND) \ + ((void *)((((intptr_t)__builtin_frame_address(0) - 1) & -GetStackSize()) + \ + (ADDEND))) /** * Returns preferred bottom address of stack. @@ -78,7 +79,7 @@ extern char ape_stack_align[] __attribute__((__weak__)); } else { \ vAddr = 0x10000000; \ } \ - vAddr; \ + (void *)vAddr; \ }) COSMOPOLITAN_C_END_ diff --git a/libc/runtime/stackuse.c b/libc/runtime/stackuse.c index 9e7138ae0..dbb1cb51a 100644 --- a/libc/runtime/stackuse.c +++ b/libc/runtime/stackuse.c @@ -16,9 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/atomic.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/str/str.h" @@ -26,16 +29,23 @@ static char stacklog[1024]; -static size_t NullLength(const char *s) { - typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); - size_t n; - xmm_t v, z = {0}; - unsigned m, k = (uintptr_t)s & 15; - const xmm_t *p = (const xmm_t *)((uintptr_t)s & -16); - m = (__builtin_ia32_pmovmskb128(*p == z) ^ 0xffff) >> k << k; - while (!m) m = __builtin_ia32_pmovmskb128(*++p == z) ^ 0xffff; - n = (const char *)p + __builtin_ctzl(m) - s; - return n; +size_t GetStackUsage(char *s, size_t n) { + // RHEL5 MAP_GROWSDOWN seems to only grow to 68kb :'( + // So we count non-zero bytes down from the top + // First clear 64 bytes is considered the end + long *p; + size_t got; + p = (long *)(s + n); + got = 0; + for (;;) { + p -= 8; + if (p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]) { + ++got; + } else { + break; + } + } + return got * 8 * sizeof(long); } static textexit void LogStackUse(void) { @@ -43,10 +53,8 @@ static textexit void LogStackUse(void) { bool quote; char *p, *q; size_t n, usage; - const char *stack; + usage = GetStackUsage(GetStackAddr(0), GetStackSize()); fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY, 0644); - stack = (char *)GetStackAddr(0); - usage = GetStackSize() - (NullLength(stack + PAGESIZE) + PAGESIZE); p = FormatUint64(stacklog, usage); for (i = 0; i < __argc; ++i) { n = strlen(__argv[i]); diff --git a/libc/runtime/untrackmemoryintervals.c b/libc/runtime/untrackmemoryintervals.c index 58d757ce4..16ccca652 100644 --- a/libc/runtime/untrackmemoryintervals.c +++ b/libc/runtime/untrackmemoryintervals.c @@ -16,17 +16,16 @@ │ 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/macros.internal.h" #include "libc/runtime/memtrack.internal.h" int UntrackMemoryIntervals(void *addr, size_t size) { int a, b; + assert(size > 0); a = ROUNDDOWN((intptr_t)addr, FRAMESIZE) >> 16; b = ROUNDDOWN((intptr_t)addr + size - 1, FRAMESIZE) >> 16; - if (SupportsWindows()) { - return ReleaseMemoryIntervals(&_mmi, a, b, ReleaseMemoryNt); - } else { - return ReleaseMemoryIntervals(&_mmi, a, b, 0); - } + return ReleaseMemoryIntervals(&_mmi, a, b, + SupportsWindows() ? ReleaseMemoryNt : 0); } diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 36c54d12c..12e599b0d 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -198,7 +198,7 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { _mmi.p = _mmi.s; _mmi.n = ARRAYLEN(_mmi.s); argsize = ROUNDUP(sizeof(struct WinArgs), FRAMESIZE); - stackaddr = GetStaticStackAddr(0); + stackaddr = (intptr_t)GetStaticStackAddr(0); stacksize = GetStackSize(); allocsize = argsize + stacksize; allocaddr = stackaddr - argsize; diff --git a/libc/sock/ntstdin.greg.c b/libc/sock/ntstdin.greg.c deleted file mode 100644 index b435a91da..000000000 --- a/libc/sock/ntstdin.greg.c +++ /dev/null @@ -1,150 +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/internal.h" -#include "libc/calls/strace.internal.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/refcount.h" -#include "libc/intrin/spinlock.h" -#include "libc/mem/mem.h" -#include "libc/nexgen32e/nt2sysv.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/errors.h" -#include "libc/nt/events.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/sock/ntstdin.internal.h" - -/** - * @fileoverview Pollable Standard Input for the New Technology. - */ - -static textwindows uint32_t StdinWorkerThread(void *arg) { - char buf[512]; - bool32 ok = true; - uint32_t i, rc, got, err, wrote; - struct NtStdinWorker w, *wp = arg; - NTTRACE("StdinWorkerThread(%ld → %ld → %ld) pid %d tid %d", wp->reader, - wp->writer, wp->consumer, getpid(), gettid()); - _spunlock(&wp->sync); - w = *wp; - do { - ok = ReadFile(w.reader, buf, sizeof(buf), &got, 0); - /* When writing to a non-blocking, byte-mode pipe handle with - insufficient buffer space, WriteFile returns TRUE with - *lpNumberOfBytesWritten < nNumberOfBytesToWrite. - ──Quoth MSDN WriteFile() */ - for (i = 0; ok && i < got; i += wrote) { - ok = WriteFile(w.writer, buf + i, got - i, &wrote, 0); - } - } while (ok && got); - err = GetLastError(); - if (!ok) { - if (err == kNtErrorHandleEof || err == kNtErrorBrokenPipe || - err == kNtErrorNoData) { - ok = true; - } - } - NTTRACE("StdinWorkerThread(%ld → %ld → %ld) → %hhhd %u", w.reader, w.writer, - w.consumer, err); - return !ok; -} - -/** - * Converts read-only file descriptor to pollable named pipe. - * - * @param fd is open file descriptor to convert - * @return new object on success, or 0 w/ errno - */ -textwindows struct NtStdinWorker *NewNtStdinWorker(int fd) { - struct NtStdinWorker *w; - NTTRACE("LaunchNtStdinWorker(%d) pid %d tid %d", fd, getpid(), gettid()); - assert(!g_fds.p[fd].worker); - assert(__isfdopen(fd)); - if (!(w = calloc(1, sizeof(struct NtStdinWorker)))) return 0; - w->refs = 1; - w->sync = 1; - w->reader = g_fds.p[fd].handle; - if ((w->consumer = CreateNamedPipe( - CreatePipeName(w->name), - kNtPipeAccessInbound | kNtFileFlagOverlapped, - kNtPipeTypeByte | kNtPipeReadmodeByte | kNtPipeRejectRemoteClients, - 1, 512, 512, 0, 0)) != -1) { - if ((w->writer = CreateFile(w->name, kNtGenericWrite, 0, 0, kNtOpenExisting, - kNtFileFlagOverlapped, 0)) != -1) { - if ((w->worker = CreateThread(0, 0, NT2SYSV(StdinWorkerThread), w, 0, - &w->tid)) != -1) { - _spinlock(&w->sync); - g_fds.p[fd].handle = w->consumer; - g_fds.p[fd].worker = w; - return w; - } - CloseHandle(w->writer); - } - CloseHandle(w->consumer); - } - free(w); - return w; -} - -/** - * References stdin worker on the New Technology. - * @param w is non-null worker object - * @return worker object for new fd - */ -textwindows struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *w) { - _incref(&w->refs); - return w; -} - -/** - * Dereferences stdin worker on the New Technology. - * @param w is non-null worker object - * @return true if ok otherwise false - */ -textwindows bool UnrefNtStdinWorker(struct NtStdinWorker *w) { - bool ok = true; - if (_decref(&w->refs)) return true; - if (!CloseHandle(w->consumer)) ok = false; - if (!CloseHandle(w->writer)) ok = false; - if (!CloseHandle(w->reader)) ok = false; - if (!CloseHandle(w->worker)) ok = false; - free(w); - return ok; -} - -/** - * Runs post fork for stdin workers on the New Technology. - */ -textwindows void ForkNtStdinWorker(void) { - for (int i = 0; i < g_fds.n; ++i) { - if (g_fds.p[i].kind && g_fds.p[i].worker) { - g_fds.p[i].handle = g_fds.p[i].worker->reader; - free(g_fds.p[i].worker); - g_fds.p[i].worker = 0; - } - } -} diff --git a/libc/sock/ntstdin.internal.h b/libc/sock/ntstdin.internal.h deleted file mode 100644 index 4afd2d023..000000000 --- a/libc/sock/ntstdin.internal.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct NtStdinWorker { /* non-inherited */ - volatile char sync; /* spin sync start */ - int refs; /* reference count */ - uint32_t tid; /* of the worker */ - int64_t reader; /* the real handle */ - int64_t writer; /* for the worker */ - int64_t worker; /* thread handle */ - int64_t consumer; /* same as Fd::handle */ - char16_t name[64]; /* for named pipe */ -}; - -struct NtStdinWorker *NewNtStdinWorker(int) hidden; -struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *) hidden; -bool UnrefNtStdinWorker(struct NtStdinWorker *) hidden; -void ForkNtStdinWorker(void) hidden; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_ */ diff --git a/libc/sock/sock.mk b/libc/sock/sock.mk index 4ffe02852..49205fc56 100644 --- a/libc/sock/sock.mk +++ b/libc/sock/sock.mk @@ -59,11 +59,6 @@ $(LIBC_SOCK_A).pkg: \ $(LIBC_SOCK_A_OBJS) \ $(foreach x,$(LIBC_SOCK_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/sock/ntstdin.greg.o: \ - OVERRIDE_COPTS += \ - -ffreestanding \ - $(NO_MAGIC) - LIBC_SOCK_LIBS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x))) LIBC_SOCK_SRCS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_SRCS)) LIBC_SOCK_HDRS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/sysv/strace.greg.c b/libc/sysv/strace.greg.c index cd75bfa5f..0822d65cb 100644 --- a/libc/sysv/strace.greg.c +++ b/libc/sysv/strace.greg.c @@ -16,6 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" +#include "libc/runtime/runtime.h" -int __strace; +/** + * System call logging enabled state. + * + * If Cosmopolitan was compiled with the `SYSDEBUG` macro (this is the + * default behavior, except in tiny and release modes) then `__strace` + * shall control whether or not system calls are logged to fd 2. If it's + * greater than zero, syscalls are logged. Otherwise, they're aren't. + * + * By convention, functions wishing to disable syscall tracing for a + * short time period should say: + * + * void foo() { + * --__strace; + * bar(); + * ++__strace; + * } + * + * This way you still have some flexibility to force syscall tracing, by + * setting `__strace` to a higher number like `2` or `200`. Even though + * under normal circumstances, `__strace` should only be either zero or + * one. + */ +_Atomic(int) __strace; diff --git a/libc/sysv/g_syscount.S b/libc/sysv/syscount.S similarity index 91% rename from libc/sysv/g_syscount.S rename to libc/sysv/syscount.S index ff253c0cf..5e14eb44f 100644 --- a/libc/sysv/g_syscount.S +++ b/libc/sysv/syscount.S @@ -26,26 +26,26 @@ // wouldn't impact this counter. .bss .align 8 -g_syscount: +__syscount: .quad 0 - .endobj g_syscount,globl + .endobj __syscount,globl .previous - .initbss 701,_init_g_syscount -g_syscount_next: + .initbss 701,_init___syscount +__syscount_next: .quad 0 - .endobj g_syscount_next + .endobj __syscount_next .previous syscount: - incq g_syscount(%rip) - jmp *g_syscount_next(%rip) + incq __syscount(%rip) + jmp *__syscount_next(%rip) .endfn syscount .previous - .init.start 701,_init_g_syscount + .init.start 701,_init___syscount mov __systemfive(%rip),%rax stosq ezlea syscount,ax mov %rax,__systemfive(%rip) - .init.end 701,_init_g_syscount + .init.end 701,_init___syscount diff --git a/libc/sysv/sysv.mk b/libc/sysv/sysv.mk index 5811daee5..65062f721 100644 --- a/libc/sysv/sysv.mk +++ b/libc/sysv/sysv.mk @@ -33,7 +33,7 @@ LIBC_SYSV_A_DIRECTDEPS = \ LIBC_SYSV_A_FILES := \ libc/sysv/macros.internal.h \ libc/sysv/errfuns.h \ - libc/sysv/g_syscount.S \ + libc/sysv/syscount.S \ libc/sysv/restorert.S \ libc/sysv/syscall.S \ libc/sysv/systemfive.S \ @@ -75,9 +75,11 @@ o/libc/sysv/consts/syscon.internal.inc: \ libc/macros-cpp.internal.inc \ libc/macros.internal.inc +# we can't use asan and ubsan because: +# we're higher in the topological order of things o/$(MODE)/libc/sysv/errno.greg.o: \ OVERRIDE_CFLAGS += \ - $(NO_MAGIC) + -fno-sanitize=all #─────────────────────────────────────────────────────────────────────────────── diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index caa89a114..cfe23c8e2 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -67,6 +67,7 @@ Flags:\n\ STATIC_YOINK("__die"); STATIC_YOINK("__get_symbol_by_addr"); STATIC_YOINK("testlib_quota_handlers"); +STATIC_YOINK("stack_usage_logging"); static bool runbenchmarks_; diff --git a/libc/thread/sem.c b/libc/thread/sem.c index e1b5bfa81..2355b8be4 100644 --- a/libc/thread/sem.c +++ b/libc/thread/sem.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/atomic.h" -#include "libc/intrin/atomic_load.h" #include "libc/thread/sem.h" #include "libc/thread/wait.h" #include "libc/thread/yield.h" @@ -74,7 +73,7 @@ int cthread_sem_wait_futex(cthread_sem_t* sem, const struct timespec* timeout) { while ((uint32_t)count > 0) { // without spin, we could miss a futex wake if (atomic_compare_exchange_weak( - &sem->linux.count, count, + &sem->linux.count, &count, count - 1 - ((uint64_t)1 << CTHREAD_THREAD_VAL_BITS))) { return 0; } @@ -97,7 +96,7 @@ int cthread_sem_wait_spin(cthread_sem_t* sem, uint64_t count, int spin, while ((uint32_t)count > 0) { // spin is useful if multiple waiters can acquire the semaphore at the // same time - if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { + if (atomic_compare_exchange_weak(&sem->linux.count, &count, count - 1)) { return 0; } } @@ -115,7 +114,7 @@ int cthread_sem_wait(cthread_sem_t* sem, int spin, while ((uint32_t)count > 0) { // spin is useful if multiple waiters can acquire the semaphore at the same // time - if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { + if (atomic_compare_exchange_weak(&sem->linux.count, &count, count - 1)) { return 0; } } diff --git a/libc/x/makedirs.c b/libc/x/makedirs.c index 1d3fd2eb8..6255bf9ff 100644 --- a/libc/x/makedirs.c +++ b/libc/x/makedirs.c @@ -29,11 +29,13 @@ static int MakeDirs(const char *path, unsigned mode, int e) { int rc; char *dir; - if (mkdir(path, mode) != -1) { + if (mkdir(path, mode) != -1 || errno == EEXIST) { errno = e; return 0; } - if (errno != ENOENT) return -1; + if (errno != ENOENT) { + return -1; + } dir = xdirname(path); if (strcmp(dir, path)) { rc = MakeDirs(dir, mode, e); @@ -49,6 +51,8 @@ static int MakeDirs(const char *path, unsigned mode, int e) { /** * Recursively creates directory a.k.a. folder. * + * This function won't fail if the directory already exists. + * * @param path is a UTF-8 string, preferably relative w/ forward slashes * @param mode can be, for example, 0755 * @return 0 on success or -1 w/ errno diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index 955d41266..6f3694a30 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -64,16 +64,16 @@ TEST(commandv, testPathSearch_appendsComExtension) { TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) { EXPECT_NE(-1, touch("home/sh", 0755)); - i = g_syscount; + i = __syscount; EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf, sizeof(pathbuf))); - if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount); + if (!IsWindows()) EXPECT_EQ(i + 1, __syscount); } TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) { EXPECT_NE(-1, touch("home/sh.com", 0755)); - i = g_syscount; + i = __syscount; EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf, sizeof(pathbuf))); - if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount); + if (!IsWindows()) EXPECT_EQ(i + 2, __syscount); } TEST(commandv, testSameDir_doesntHappenByDefaultUnlessItsWindows) { diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index e0037d558..07aa908f2 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -34,30 +34,31 @@ void SetUp(void) { } TEST(mkdir, testNothingExists_ENOENT) { - EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755)); - EXPECT_EQ(ENOENT, errno); + EXPECT_SYS(ENOENT, -1, mkdir("yo/yo/yo", 0755)); } TEST(mkdir, testDirectoryComponentIsFile_ENOTDIR) { - EXPECT_NE(-1, touch("yo", 0644)); - EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755)); - EXPECT_EQ(ENOTDIR, errno); + EXPECT_SYS(0, 0, touch("yo", 0644)); + EXPECT_SYS(ENOTDIR, -1, mkdir("yo/yo/yo", 0755)); } TEST(mkdir, testPathIsFile_EEXIST) { - EXPECT_NE(-1, mkdir("yo", 0755)); - EXPECT_NE(-1, mkdir("yo/yo", 0755)); - EXPECT_NE(-1, touch("yo/yo/yo", 0644)); - EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755)); - EXPECT_EQ(EEXIST, errno); + EXPECT_SYS(0, 0, mkdir("yo", 0755)); + EXPECT_SYS(0, 0, mkdir("yo/yo", 0755)); + EXPECT_SYS(0, 0, touch("yo/yo/yo", 0644)); + EXPECT_SYS(EEXIST, -1, mkdir("yo/yo/yo", 0755)); } TEST(mkdir, testPathIsDirectory_EEXIST) { - EXPECT_NE(-1, mkdir("yo", 0755)); - EXPECT_NE(-1, mkdir("yo/yo", 0755)); - EXPECT_NE(-1, mkdir("yo/yo/yo", 0755)); - EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755)); - EXPECT_EQ(EEXIST, errno); + EXPECT_SYS(0, 0, mkdir("yo", 0755)); + EXPECT_SYS(0, 0, mkdir("yo/yo", 0755)); + EXPECT_SYS(0, 0, mkdir("yo/yo/yo", 0755)); + EXPECT_SYS(EEXIST, -1, mkdir("yo/yo/yo", 0755)); +} + +TEST(makedirs, pathExists_isSuccess) { + EXPECT_SYS(0, 0, makedirs("foo/bar", 0755)); + EXPECT_SYS(0, 0, makedirs("foo/bar", 0755)); } TEST(mkdir, enametoolong) { @@ -69,19 +70,17 @@ TEST(mkdir, enametoolong) { EXPECT_SYS(ENAMETOOLONG, -1, mkdir(s, 0644)); } -TEST(makedirs, testEmptyString_EEXIST) { - EXPECT_EQ(-1, mkdir("", 0755)); - EXPECT_EQ(ENOENT, errno); +TEST(makedirs, testEmptyString_ENOENT) { + EXPECT_SYS(ENOENT, -1, mkdir("", 0755)); } TEST(mkdirat, testRelativePath_opensRelativeToDirFd) { int dirfd; - ASSERT_NE(-1, mkdir("foo", 0755)); - ASSERT_NE(-1, (dirfd = open("foo", O_RDONLY | O_DIRECTORY))); - EXPECT_NE(-1, mkdirat(dirfd, "bar", 0755)); + ASSERT_SYS(0, 0, mkdir("foo", 0755)); + ASSERT_SYS(0, 3, (dirfd = open("foo", O_RDONLY | O_DIRECTORY))); + EXPECT_SYS(0, 0, mkdirat(dirfd, "bar", 0755)); EXPECT_TRUE(isdirectory("foo/bar")); - EXPECT_EQ(-1, makedirs("", 0755)); - EXPECT_NE(-1, close(dirfd)); + EXPECT_SYS(0, 0, close(dirfd)); } TEST(mkdir, longname) { diff --git a/test/libc/fmt/lengthuint64_test.c b/test/libc/fmt/lengthuint64_test.c index f0ddbd48f..7c4b2cee9 100644 --- a/test/libc/fmt/lengthuint64_test.c +++ b/test/libc/fmt/lengthuint64_test.c @@ -94,9 +94,13 @@ TEST(LengthInt64Thousands, test) { } BENCH(LengthInt64, bench) { - EZBENCH2("LengthInt64", donothing, LengthInt64(INT64_MIN)); - EZBENCH2("LengthUint64", donothing, LengthUint64(UINT64_MAX)); - EZBENCH2("LengthInt64Thousands", donothing, LengthInt64Thousands(INT64_MIN)); + unsigned LengthInt64_(int64_t) asm("LengthInt64"); + unsigned LengthUint64_(uint64_t) asm("LengthUint64"); + unsigned LengthInt64Thousands_(int64_t) asm("LengthInt64Thousands"); + unsigned LengthUint64Thousands_(uint64_t) asm("LengthUint64Thousands"); + EZBENCH2("LengthInt64", donothing, LengthInt64_(INT64_MIN)); + EZBENCH2("LengthUint64", donothing, LengthUint64_(UINT64_MAX)); + EZBENCH2("LengthInt64Thousands", donothing, LengthInt64Thousands_(INT64_MIN)); EZBENCH2("LengthUint64Thousands", donothing, - LengthUint64Thousands(UINT64_MAX)); + LengthUint64Thousands_(UINT64_MAX)); } diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index 071c15f5d..7618d68da 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -43,8 +43,7 @@ */ static uint64_t Rando(void) { uint64_t x; - do - x = lemur64(); + do x = lemur64(); while (((x ^ READ64LE("!!!!!!!!")) - 0x0101010101010101) & ~(x ^ READ64LE("!!!!!!!!")) & 0x8080808080808080); return x; @@ -249,9 +248,9 @@ TEST(kprintf, testFailure_wontClobberErrnoAndBypassesSystemCallSupport) { ASSERT_EQ(0, errno); EXPECT_SYS(0, 3, dup(2)); EXPECT_SYS(0, 0, close(2)); - n = g_syscount; + n = __syscount; kprintf("hello%n"); - EXPECT_EQ(n, g_syscount); + EXPECT_EQ(n, __syscount); EXPECT_EQ(0, errno); EXPECT_SYS(0, 2, dup2(3, 2)); EXPECT_SYS(0, 0, close(3)); diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index 90885a554..7311543a4 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/threaded.h" @@ -40,20 +41,26 @@ #define ENTRIES 256 char locks[THREADS]; -volatile bool ready; +_Atomic(bool) ready; volatile uint64_t A[THREADS * ENTRIES]; void OnChld(int sig) { // do nothing } +dontinline void Pause(void) { + __builtin_ia32_pause(); +} + +dontinline void Generate(int i) { + A[i] = rand64(); +} + int Thrasher(void *arg) { int i, id = (intptr_t)arg; - while (!ready) { - __builtin_ia32_pause(); - } + while (!ready) Pause(); for (i = 0; i < ENTRIES; ++i) { - A[id * ENTRIES + i] = rand64(); + Generate(id * ENTRIES + i); } _spunlock(locks + id); return 0; diff --git a/test/libc/runtime/memtrack_test.c b/test/libc/runtime/memtrack_test.c index 200ec8a2e..a9c9a4888 100644 --- a/test/libc/runtime/memtrack_test.c +++ b/test/libc/runtime/memtrack_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/log/check.h" #include "libc/mem/mem.h" @@ -26,6 +27,9 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" +#define I(x, y) \ + { x, y, 0, (y - x) * FRAMESIZE + FRAMESIZE } + static bool AreMemoryIntervalsEqual(const struct MemoryIntervals *mm1, const struct MemoryIntervals *mm2) { if (mm1->i != mm2->i) return false; @@ -45,8 +49,10 @@ static void PrintMemoryInterval(const struct MemoryIntervals *mm) { static void CheckMemoryIntervalsEqual(const struct MemoryIntervals *mm1, const struct MemoryIntervals *mm2) { if (!AreMemoryIntervalsEqual(mm1, mm2)) { - PrintMemoryInterval(mm1); - PrintMemoryInterval(mm2); + kprintf("got:\n"); + PrintMemoryIntervals(2, mm1); + kprintf("want:\n"); + PrintMemoryIntervals(2, mm2); CHECK(!"memory intervals not equal"); exit(1); } @@ -65,7 +71,8 @@ static void RunTrackMemoryIntervalTest(const struct MemoryIntervals t[2], int x, struct MemoryIntervals *mm; mm = memcpy(memalign(64, sizeof(*t)), t, sizeof(*t)); CheckMemoryIntervalsAreOk(mm); - CHECK_NE(-1, TrackMemoryInterval(mm, x, y, h, 0, 0, 0, 0, 0, 0)); + CHECK_NE(-1, TrackMemoryInterval(mm, x, y, h, 0, 0, 0, 0, 0, + (y - x) * FRAMESIZE + FRAMESIZE)); CheckMemoryIntervalsAreOk(mm); CheckMemoryIntervalsEqual(mm, t + 1); free(mm); @@ -88,7 +95,7 @@ static int RunReleaseMemoryIntervalsTest(const struct MemoryIntervals t[2], TEST(TrackMemoryInterval, TestEmpty) { static struct MemoryIntervals mm[2] = { {0, OPEN_MAX, 0, {}}, - {1, OPEN_MAX, 0, {{2, 2, 0}}}, + {1, OPEN_MAX, 0, {{2, 2, 0, FRAMESIZE}}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -114,8 +121,8 @@ TEST(TrackMemoryInterval, TestFull) { TEST(TrackMemoryInterval, TestAppend) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{2, 2}}}, - {1, OPEN_MAX, 0, {{2, 3}}}, + {1, OPEN_MAX, 0, {I(2, 2)}}, + {1, OPEN_MAX, 0, {I(2, 3)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -124,8 +131,8 @@ TEST(TrackMemoryInterval, TestAppend) { TEST(TrackMemoryInterval, TestPrepend) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{2, 2}}}, - {1, OPEN_MAX, 0, {{1, 2}}}, + {1, OPEN_MAX, 0, {I(2, 2)}}, + {1, OPEN_MAX, 0, {I(1, 2)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -134,8 +141,8 @@ TEST(TrackMemoryInterval, TestPrepend) { TEST(TrackMemoryInterval, TestFillHole) { static struct MemoryIntervals mm[2] = { - {4, OPEN_MAX, 0, {{1, 1}, {3, 4}, {5, 5, 1}, {6, 8}}}, - {3, OPEN_MAX, 0, {{1, 4}, {5, 5, 1}, {6, 8}}}, + {4, OPEN_MAX, 0, {I(1, 1), I(3, 4), {5, 5, 1, FRAMESIZE}, I(6, 8)}}, + {3, OPEN_MAX, 0, {I(1, 4), {5, 5, 1, FRAMESIZE}, I(6, 8)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -144,8 +151,8 @@ TEST(TrackMemoryInterval, TestFillHole) { TEST(TrackMemoryInterval, TestAppend2) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{2, 2}}}, - {2, OPEN_MAX, 0, {{2, 2}, {3, 3, 1}}}, + {1, OPEN_MAX, 0, {I(2, 2)}}, + {2, OPEN_MAX, 0, {I(2, 2), {3, 3, 1, FRAMESIZE}}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -154,8 +161,8 @@ TEST(TrackMemoryInterval, TestAppend2) { TEST(TrackMemoryInterval, TestPrepend2) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{2, 2}}}, - {2, OPEN_MAX, 0, {{1, 1, 1}, {2, 2}}}, + {1, OPEN_MAX, 0, {I(2, 2)}}, + {2, OPEN_MAX, 0, {{1, 1, 1, FRAMESIZE}, I(2, 2)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -164,8 +171,25 @@ TEST(TrackMemoryInterval, TestPrepend2) { TEST(TrackMemoryInterval, TestFillHole2) { static struct MemoryIntervals mm[2] = { - {4, OPEN_MAX, 0, {{1, 1}, {3, 4}, {5, 5, 1}, {6, 8}}}, - {5, OPEN_MAX, 0, {{1, 1}, {2, 2, 1}, {3, 4}, {5, 5, 1}, {6, 8}}}, + {4, + OPEN_MAX, + 0, + { + I(1, 1), + I(3, 4), + {5, 5, 1, FRAMESIZE}, + I(6, 8), + }}, + {5, + OPEN_MAX, + 0, + { + I(1, 1), + {2, 2, 1, FRAMESIZE}, + {3, 4, 0, FRAMESIZE * 2}, + {5, 5, 1, FRAMESIZE}, + {6, 8, 0, FRAMESIZE * 3}, + }}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -211,8 +235,8 @@ TEST(ReleaseMemoryIntervals, TestEmpty) { TEST(ReleaseMemoryIntervals, TestRemoveElement_UsesInclusiveRange) { static struct MemoryIntervals mm[2] = { - {3, OPEN_MAX, 0, {{0, 0}, {2, 2}, {4, 4}}}, - {2, OPEN_MAX, 0, {{0, 0}, {4, 4}}}, + {3, OPEN_MAX, 0, {I(0, 0), I(2, 2), I(4, 4)}}, + {2, OPEN_MAX, 0, {I(0, 0), I(4, 4)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -221,8 +245,8 @@ TEST(ReleaseMemoryIntervals, TestRemoveElement_UsesInclusiveRange) { TEST(ReleaseMemoryIntervals, TestPunchHole) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{0, 9}}}, - {2, OPEN_MAX, 0, {{0, 3}, {6, 9}}}, + {1, OPEN_MAX, 0, {I(0, 9)}}, + {2, OPEN_MAX, 0, {I(0, 3), I(6, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -232,8 +256,8 @@ TEST(ReleaseMemoryIntervals, TestPunchHole) { TEST(ReleaseMemoryIntervals, TestShortenLeft) { if (IsWindows()) return; static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{0, 9}}}, - {1, OPEN_MAX, 0, {{0, 7}}}, + {1, OPEN_MAX, 0, {I(0, 9)}}, + {1, OPEN_MAX, 0, {I(0, 7)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -243,8 +267,8 @@ TEST(ReleaseMemoryIntervals, TestShortenLeft) { TEST(ReleaseMemoryIntervals, TestShortenRight) { if (IsWindows()) return; static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{0, 9}}}, - {1, OPEN_MAX, 0, {{3, 9}}}, + {1, OPEN_MAX, 0, {I(0, 9)}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -254,8 +278,8 @@ TEST(ReleaseMemoryIntervals, TestShortenRight) { TEST(ReleaseMemoryIntervals, TestShortenLeft2) { if (IsWindows()) return; static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{0, 9}}}, - {1, OPEN_MAX, 0, {{0, 7}}}, + {1, OPEN_MAX, 0, {I(0, 9)}}, + {1, OPEN_MAX, 0, {I(0, 7)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -265,8 +289,8 @@ TEST(ReleaseMemoryIntervals, TestShortenLeft2) { TEST(ReleaseMemoryIntervals, TestShortenRight2) { if (IsWindows()) return; static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{0, 9}}}, - {1, OPEN_MAX, 0, {{3, 9}}}, + {1, OPEN_MAX, 0, {I(0, 9)}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -275,8 +299,8 @@ TEST(ReleaseMemoryIntervals, TestShortenRight2) { TEST(ReleaseMemoryIntervals, TestZeroZero) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{3, 9}}}, - {1, OPEN_MAX, 0, {{3, 9}}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -285,8 +309,8 @@ TEST(ReleaseMemoryIntervals, TestZeroZero) { TEST(ReleaseMemoryIntervals, TestNoopLeft) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{3, 9}}}, - {1, OPEN_MAX, 0, {{3, 9}}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -295,8 +319,8 @@ TEST(ReleaseMemoryIntervals, TestNoopLeft) { TEST(ReleaseMemoryIntervals, TestNoopRight) { static struct MemoryIntervals mm[2] = { - {1, OPEN_MAX, 0, {{3, 9}}}, - {1, OPEN_MAX, 0, {{3, 9}}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, + {1, OPEN_MAX, 0, {I(3, 9)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; @@ -305,7 +329,7 @@ TEST(ReleaseMemoryIntervals, TestNoopRight) { TEST(ReleaseMemoryIntervals, TestBigFree) { static struct MemoryIntervals mm[2] = { - {2, OPEN_MAX, 0, {{0, 3}, {6, 9}}}, + {2, OPEN_MAX, 0, {I(0, 3), I(6, 9)}}, {0, OPEN_MAX, 0, {}}, }; mm[0].p = mm[0].s; @@ -315,8 +339,8 @@ TEST(ReleaseMemoryIntervals, TestBigFree) { TEST(ReleaseMemoryIntervals, TestWeirdGap) { static struct MemoryIntervals mm[2] = { - {3, OPEN_MAX, 0, {{10, 10}, {20, 20}, {30, 30}}}, - {2, OPEN_MAX, 0, {{10, 10}, {30, 30}}}, + {3, OPEN_MAX, 0, {I(10, 10), I(20, 20), I(30, 30)}}, + {2, OPEN_MAX, 0, {I(10, 10), I(30, 30)}}, }; mm[0].p = mm[0].s; mm[1].p = mm[1].s; diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index a777fdcc2..046eba15d 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -16,9 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/atomic.h" #include "libc/bits/bits.h" #include "libc/bits/xchg.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/intrin/kprintf.h" @@ -36,9 +38,12 @@ #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" +#include "third_party/xed/x86.h" char testlib_enable_tmp_setup_teardown; diff --git a/test/libc/runtime/munmap_test.c b/test/libc/runtime/munmap_test.c new file mode 100644 index 000000000..9c2998e68 --- /dev/null +++ b/test/libc/runtime/munmap_test.c @@ -0,0 +1,232 @@ +/*-*- 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/bits/atomic.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/ucontext.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sa.h" +#include "libc/testlib/testlib.h" +#include "third_party/xed/x86.h" + +int gotsignal; +char testlib_enable_tmp_setup_teardown; + +void ContinueOnError(int sig, siginfo_t *si, ucontext_t *ctx) { + struct XedDecodedInst xedd; + xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); + xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); + ctx->uc_mcontext.rip += xedd.length; + gotsignal = sig; +} + +noasan bool MemoryExists(char *p) { + volatile char c; + struct sigaction old[2]; + struct sigaction sa = { + .sa_sigaction = ContinueOnError, + .sa_flags = SA_SIGINFO, + }; + gotsignal = 0; + sigaction(SIGSEGV, &sa, old + 0); + sigaction(SIGBUS, &sa, old + 1); + c = atomic_load(p); + sigaction(SIGSEGV, old + 0, 0); + sigaction(SIGBUS, old + 1, 0); + return !gotsignal; +} + +TEST(munmap, doesntExist_doesntCare) { + EXPECT_SYS(0, 0, munmap(0, FRAMESIZE * 8)); + if (IsAsan()) { + // make sure it didn't unmap the null pointer shadow memory + EXPECT_TRUE(MemoryExists((char *)0x7fff8000)); + } +} + +TEST(munmap, test) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists(p)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_FALSE(MemoryExists(p)); +} + +TEST(munmap, invalidParams) { + EXPECT_SYS(EINVAL, -1, munmap(0, 0)); + EXPECT_SYS(EINVAL, -1, munmap((void *)0x100080000000, 0)); + EXPECT_SYS(EINVAL, -1, munmap((void *)0x100080000001, FRAMESIZE)); +} + +TEST(munmap, punchHoleInMemory) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); +} + +TEST(munmap, memoryHasHole) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); +} + +TEST(munmap, blanketFree) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 0, FRAMESIZE)); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); +} + +TEST(munmap, trimLeft) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); +} + +TEST(munmap, trimRight) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); + EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); + EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); +} + +TEST(munmap, memoryGone) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + +TEST(munmap, testTooSmallToUnmapAsan) { + if (!IsAsan()) return; + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_TRUE(MemoryExists((char *)(((intptr_t)p >> 3) + 0x7fff8000))); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_TRUE(MemoryExists((char *)(((intptr_t)p >> 3) + 0x7fff8000))); +} + +TEST(munmap, testLargeEnoughToUnmapAsan) { + if (!IsAsan()) return; + if (IsWindows()) { + // we're unfortunately never able to unmap asan pages on windows + // because the memtrack array items always have to be 64kb so we + // we're able to store a handle for each + return; + } + char *p; + size_t n; + n = FRAMESIZE * 8 * 2; + ASSERT_NE(MAP_FAILED, (p = mmap(0, n, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + EXPECT_SYS(0, 0, munmap(p, n)); +#if 0 + EXPECT_FALSE( + MemoryExists((char *)(((intptr_t)(p + n / 2) >> 3) + 0x7fff8000))); +#endif +} + +TEST(munmap, tinyFile_roundupUnmapSize) { + char *p; + ASSERT_SYS(0, 3, open("doge", O_WRONLY | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS(0, 5, write(3, "hello", 5)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, open("doge", O_RDONLY)); + ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); + ASSERT_SYS(0, 0, close(3)); + EXPECT_TRUE(MemoryExists(p)); + // some kernels/versions support this, some don't + // EXPECT_FALSE(MemoryExists(p + 5)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); + EXPECT_FALSE(MemoryExists(p)); + EXPECT_FALSE(MemoryExists(p + 5)); +} + +TEST(munmap, tinyFile_preciseUnmapSize) { + char *p, *q; + ASSERT_SYS(0, 3, open("doge", O_WRONLY | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS(0, 5, write(3, "hello", 5)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, open("doge", O_RDONLY)); + ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); + ASSERT_NE(MAP_FAILED, (q = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); + ASSERT_SYS(0, 0, close(3)); + EXPECT_TRUE(MemoryExists(p)); + EXPECT_TRUE(MemoryExists(q)); + EXPECT_SYS(0, 0, munmap(p, 5)); + EXPECT_FALSE(MemoryExists(p)); + EXPECT_TRUE(MemoryExists(q)); + EXPECT_SYS(0, 0, munmap(q, 5)); + EXPECT_FALSE(MemoryExists(q)); +} diff --git a/test/libc/str/undeflate_test.c b/test/libc/str/undeflate_test.c index 2b2c0c9a1..742e34083 100644 --- a/test/libc/str/undeflate_test.c +++ b/test/libc/str/undeflate_test.c @@ -53,20 +53,20 @@ TEST(undeflate, testStatCentralDirectory_notFound_noSysCalls) { uint64_t c; struct stat st; stat("/zip/doge.txt", &st); /* warmup */ - c = g_syscount; + c = __syscount; ASSERT_EQ(-1, stat("/zip/doge.txt", &st)); - ASSERT_EQ(0, g_syscount - c); + ASSERT_EQ(0, __syscount - c); ASSERT_EQ(ENOENT, errno); } TEST(undeflate, testStatCentralDirectory_isFound_noSysCalls) { uint64_t c; struct stat st = {0}; - c = g_syscount; + c = __syscount; ASSERT_NE(-1, stat("/zip/libc/testlib/hyperion.txt", &st)); ASSERT_TRUE(S_ISREG(st.st_mode)); ASSERT_EQ(kHyperionSize, st.st_size); - ASSERT_EQ(0, g_syscount - c); + ASSERT_EQ(0, __syscount - c); } TEST(undeflate, testOpenReadCloseEmbeddedZip) { diff --git a/test/tool/plinko/plinko_test.c b/test/tool/plinko/plinko_test.c index 4b95011b0..f9f11a036 100644 --- a/test/tool/plinko/plinko_test.c +++ b/test/tool/plinko/plinko_test.c @@ -70,7 +70,6 @@ TEST(plinko, worksOrPrintsNiceError) { sigset_t chldmask, savemask; int i, pid, fdin, wstatus, pfds[2][2]; struct sigaction ignore, saveint, savequit, savepipe; - bzero(buf, sizeof(buf)); ignore.sa_flags = 0; ignore.sa_handler = SIG_IGN; EXPECT_EQ(0, sigemptyset(&ignore.sa_mask)); @@ -82,8 +81,10 @@ TEST(plinko, worksOrPrintsNiceError) { EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); ASSERT_NE(-1, pipe2(pfds[0], O_CLOEXEC)); ASSERT_NE(-1, pipe2(pfds[1], O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { + __strace = 0; + __ftrace = 0; close(0), dup(pfds[0][0]); close(1), dup(pfds[1][1]); close(2), dup(pfds[1][1]); @@ -104,7 +105,8 @@ TEST(plinko, worksOrPrintsNiceError) { EXPECT_NE(-1, close(fdin)); } EXPECT_NE(-1, close(pfds[0][1])); - EXPECT_NE(-1, (got = read(pfds[1][0], buf, sizeof(buf) - 1))); + bzero(buf, sizeof(buf)); + ASSERT_NE(-1, (got = read(pfds[1][0], buf, sizeof(buf) - 1))); EXPECT_NE(0, got); while (read(pfds[1][0], drain, sizeof(drain)) > 0) donothing; EXPECT_NE(-1, close(pfds[1][0])); diff --git a/third_party/dlmalloc/dlmalloc.greg.c b/third_party/dlmalloc/dlmalloc.c similarity index 99% rename from third_party/dlmalloc/dlmalloc.greg.c rename to third_party/dlmalloc/dlmalloc.c index 5d34cc0e2..b5f76f6b4 100644 --- a/third_party/dlmalloc/dlmalloc.greg.c +++ b/third_party/dlmalloc/dlmalloc.c @@ -20,7 +20,7 @@ #include "third_party/dlmalloc/dlmalloc.h" // clang-format off -#define FOOTERS 1 +#define FOOTERS 0 #define MSPACES 0 #define HAVE_MMAP 1 @@ -351,7 +351,7 @@ unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); # endif # endif # ifdef _SC_PAGE_SIZE -# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# define malloc_getpagesize 4096 /*sysconf(_SC_PAGE_SIZE)*/ # else # if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) extern size_t getpagesize(); @@ -1514,7 +1514,10 @@ static int init_mparams(void) { size_t psize; size_t gsize; -#ifndef WIN32 +#if defined(__COSMOPOLITAN__) + psize = 4096; + gsize = 65536; +#elif !defined(WIN32) psize = malloc_getpagesize; gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); #else /* WIN32 */ diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index 2c27169fa..d2cc826bb 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -49,10 +49,17 @@ $(THIRD_PARTY_DLMALLOC_A).pkg: \ $(THIRD_PARTY_DLMALLOC_A_OBJS) \ $(foreach x,$(THIRD_PARTY_DLMALLOC_A_DIRECTDEPS),$($(x)_A).pkg) -$(THIRD_PARTY_DLMALLOC_A_OBJS): \ +# we can't use address sanitizer because: +# address sanitizer depends on dlmalloc +o/$(MODE)/third_party/dlmalloc/dlmalloc.o: \ OVERRIDE_CFLAGS += \ - $(NO_MAGIC) \ -ffreestanding \ + -fno-sanitize=address + +# we must segregate codegen because: +# file contains multiple independently linkable apis +o/$(MODE)/third_party/dlmalloc/dlmalloc.greg.o: \ + OVERRIDE_CFLAGS += \ -ffunction-sections \ -fdata-sections diff --git a/third_party/dlmalloc/vespene.greg.c b/third_party/dlmalloc/vespene.greg.c index bbcbd0989..da84f24cc 100644 --- a/third_party/dlmalloc/vespene.greg.c +++ b/third_party/dlmalloc/vespene.greg.c @@ -33,7 +33,7 @@ void *dlmalloc_requires_more_vespene_gas(size_t size) { if ((p = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { if (weaken(__asan_poison)) { - weaken(__asan_poison)((uintptr_t)p, size, kAsanHeapFree); + weaken(__asan_poison)(p, size, kAsanHeapFree); } } return p; diff --git a/third_party/python/Include/ceval.h b/third_party/python/Include/ceval.h index 0822e0c78..47eaf797b 100644 --- a/third_party/python/Include/ceval.h +++ b/third_party/python/Include/ceval.h @@ -119,7 +119,7 @@ int _Py_CheckRecursiveCall(const char *); rc = _Py_CheckRecursiveCall(where); \ } else { \ rsp = (intptr_t)__builtin_frame_address(0); \ - bot = GetStackAddr(32768); \ + bot = (intptr_t)GetStackAddr(32768); \ if (UNLIKELY(rsp < bot)) { \ PyErr_Format(PyExc_MemoryError, "Stack overflow%s", where); \ rc = -1; \ diff --git a/third_party/python/Lib/test/test_coroutines.py b/third_party/python/Lib/test/test_coroutines.py index 291af248e..5bc5a1d80 100644 --- a/third_party/python/Lib/test/test_coroutines.py +++ b/third_party/python/Lib/test/test_coroutines.py @@ -1199,7 +1199,10 @@ class CoroutineTest(unittest.TestCase): with self.assertRaisesRegex(AttributeError, '__aexit__'): run_async(foo()) - @unittest.skipIf("tiny" in cosmo.MODE, "TODO: figure out error") + # TODO(jart,ahgamut): Figure out this error. + @unittest.skipIf(cosmo.MODE in ('tiny', 'rel'), + "No docstrings in MODE=tiny/rel") + @unittest.skipIf("tiny" in cosmo.MODE, "") def test_with_5(self): # While this test doesn't make a lot of sense, # it's a regression test for an early bug with opcodes diff --git a/third_party/python/Lib/test/test_signal.py b/third_party/python/Lib/test/test_signal.py index 7c2db9890..37d10e56b 100644 --- a/third_party/python/Lib/test/test_signal.py +++ b/third_party/python/Lib/test/test_signal.py @@ -488,7 +488,6 @@ class SiginterruptTest(unittest.TestCase): try: # wait until the child process is loaded and has started first_line = process.stdout.readline() - stdout, stderr = process.communicate(timeout=5.0) except subprocess.TimeoutExpired: process.kill() @@ -515,12 +514,13 @@ class SiginterruptTest(unittest.TestCase): interrupted = self.readpipe_interrupted(True) self.assertTrue(interrupted) - def test_siginterrupt_off(self): - # If a signal handler is installed and siginterrupt is called with - # a false value for the second argument, when that signal arrives, it - # does not interrupt a syscall that's in progress. - interrupted = self.readpipe_interrupted(False) - self.assertFalse(interrupted) + # [jart]: lool a test that takes 5 seconds by design + # def test_siginterrupt_off(self): + # # If a signal handler is installed and siginterrupt is called with + # # a false value for the second argument, when that signal arrives, it + # # does not interrupt a syscall that's in progress. + # interrupted = self.readpipe_interrupted(False) + # self.assertFalse(interrupted) @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") diff --git a/third_party/python/Python/cosmomodule.c b/third_party/python/Python/cosmomodule.c index 71f795805..b6ea626c5 100644 --- a/third_party/python/Python/cosmomodule.c +++ b/third_party/python/Python/cosmomodule.c @@ -80,7 +80,7 @@ polyfilled yet."); static PyObject * cosmo_syscount(PyObject *self, PyObject *noargs) { - return PyLong_FromSize_t(g_syscount); + return PyLong_FromSize_t(__syscount); } PyDoc_STRVAR(rdtsc_doc, @@ -215,13 +215,13 @@ static int FtracerObject_init(PyObject* self, PyObject *args, PyObject *kwargs) static PyObject* FtracerObject_enter(PyObject *self, PyObject *Py_UNUSED(ignored)) { - ++g_ftrace; + ++__ftrace; return self; } static PyObject* FtracerObject_exit(PyObject *self, PyObject *args) { - --g_ftrace; + --__ftrace; return self; } diff --git a/third_party/python/python.c b/third_party/python/python.c index cb3cf1aeb..feab2bbda 100644 --- a/third_party/python/python.c +++ b/third_party/python/python.c @@ -1,6 +1,13 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Python 3 │ +│ https://docs.python.org/3/license.html │ +╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/python/Include/yoink.h" - -STATIC_YOINK("RunPythonModule"); +#include "third_party/python/runpythonmodule.h" +#include "tool/args/args.h" +// clang-format off PYTHON_YOINK("xed"); PYTHON_YOINK("xterm"); @@ -516,3 +523,10 @@ PYTHON_YOINK("asyncio.unix_events"); PYTHON_YOINK("asyncio.windows_events"); PYTHON_YOINK("asyncio.windows_utils"); #endif + +int +main(int argc, char **argv) +{ + LoadZipArgs(&argc, &argv); + return RunPythonModule(argc, argv); +} diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 7d193ca23..34f4d6b8f 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -47,6 +47,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_OBJS = \ $(THIRD_PARTY_PYTHON_STAGE1_A_SRCS:%.c=o/$(MODE)/%.o) THIRD_PARTY_PYTHON_HDRS = \ + third_party/python/runpythonmodule.h \ third_party/python/Include/ezprint.h \ third_party/python/Include/yoink.h \ third_party/python/Include/object.h \ @@ -509,7 +510,7 @@ THIRD_PARTY_PYTHON_STAGE2_A_DATA_OBJS = \ third_party/python/Lib/.zip.o THIRD_PARTY_PYTHON_STAGE2_A_SRCS = \ - third_party/python/repl.c \ + third_party/python/runpythonmodule.c \ third_party/python/launch.c \ third_party/python/Objects/fromfd.c \ third_party/python/Objects/unicodeobject-deadcode.c \ @@ -2086,6 +2087,7 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DIRECTDEPS = \ LIBC_STR \ LIBC_UNICODE \ LIBC_STDIO \ + LIBC_CALLS \ LIBC_RUNTIME \ THIRD_PARTY_PYTHON_STAGE1 \ THIRD_PARTY_PYTHON_STAGE2 \ @@ -2099,7 +2101,7 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DEPS = \ o/$(MODE)/third_party/python/pythontester.pkg: \ $(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_OBJS) \ - o/$(MODE)/third_party/python/repl.o \ + o/$(MODE)/third_party/python/pythontester.o \ $(foreach x,$(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DIRECTDEPS),$($(x)_A).pkg) o/$(MODE)/third_party/python/pythontester.com.dbg: \ @@ -2107,7 +2109,7 @@ o/$(MODE)/third_party/python/pythontester.com.dbg: \ $(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DEPS) \ $(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_OBJS) \ $(THIRD_PARTY_PYTHON_PYTEST_TODOS:%.py=o/$(MODE)/%.o) \ - o/$(MODE)/third_party/python/repl.o \ + o/$(MODE)/third_party/python/pythontester.o \ $(CRT) \ $(APE) @$(APELINK) diff --git a/third_party/python/pythontester.c b/third_party/python/pythontester.c new file mode 100644 index 000000000..45db63426 --- /dev/null +++ b/third_party/python/pythontester.c @@ -0,0 +1,19 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Python 3 │ +│ https://docs.python.org/3/license.html │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/runtime.h" +#include "third_party/python/Include/yoink.h" +#include "third_party/python/runpythonmodule.h" +#include "tool/args/args.h" +// clang-format off + +int +main(int argc, char **argv) +{ + LoadZipArgs(&argc, &argv); + __nosync = 0x5453455454534146; + return RunPythonModule(argc, argv); +} diff --git a/third_party/python/repl.c b/third_party/python/repl.c index d488fc408..412790e79 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -4,357 +4,10 @@ │ Python 3 │ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#define PY_SSIZE_T_CLEAN -#include "libc/bits/bits.h" -#include "libc/bits/safemacros.internal.h" -#include "libc/bits/weaken.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/calls/sigbits.h" -#include "libc/calls/struct/siginfo.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/ucontext.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/log/check.h" -#include "libc/log/log.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/gc.internal.h" -#include "libc/runtime/runtime.h" -#include "libc/runtime/symbols.internal.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/fileno.h" -#include "libc/sysv/consts/prot.h" -#include "libc/sysv/consts/sig.h" -#include "libc/time/time.h" -#include "libc/unicode/locale.h" -#include "libc/x/x.h" -#include "third_party/linenoise/linenoise.h" -#include "third_party/python/Include/abstract.h" -#include "third_party/python/Include/ceval.h" -#include "third_party/python/Include/dictobject.h" -#include "third_party/python/Include/fileutils.h" -#include "third_party/python/Include/funcobject.h" -#include "third_party/python/Include/import.h" -#include "third_party/python/Include/listobject.h" -#include "third_party/python/Include/moduleobject.h" -#include "third_party/python/Include/object.h" -#include "third_party/python/Include/pydebug.h" -#include "third_party/python/Include/pyerrors.h" -#include "third_party/python/Include/pylifecycle.h" -#include "third_party/python/Include/pymem.h" -#include "third_party/python/Include/pyport.h" -#include "third_party/python/Include/pythonrun.h" -#include "third_party/python/Include/unicodeobject.h" #include "third_party/python/Include/yoink.h" -#include "third_party/xed/x86.h" +#include "third_party/python/runpythonmodule.h" #include "tool/args/args.h" -/* clang-format off */ - -STATIC_STACK_SIZE(0x100000); - -STATIC_YOINK("__die"); -STATIC_YOINK("zip_uri_support"); - -PYTHON_YOINK("cosmo"); -PYTHON_YOINK("_locale"); -PYTHON_YOINK("_bootlocale"); -PYTHON_YOINK("encodings.aliases"); -PYTHON_YOINK("encodings.latin_1"); -PYTHON_YOINK("encodings.utf_8"); - -extern char kLaunchPythonModuleName[]; /* optionally generated by pyobj.com */ -const struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules; -struct _inittab *PyImport_Inittab = _PyImport_Inittab; -static int g_gotint; - -static void -OnKeyboardInterrupt(int sig) -{ - g_gotint = sig; -} - -static void -AddCompletion(linenoiseCompletions *c, char *s) -{ - char **p = c->cvec; - size_t n = c->len + 1; - if ((p = realloc(p, n * sizeof(*p)))) { - p[n - 1] = s; - c->cvec = p; - c->len = n; - } -} - -static void -CompleteModule(const char *s, const char *p, linenoiseCompletions *c) -{ - const char *name; - struct _inittab *it; - Py_ssize_t plen, namelen; - PyObject *m, *f, *g, *i, *v, *n; - plen = strlen(p); - for (it = PyImport_Inittab; it->name; ++it) { - if (startswithi(it->name, p)) { - AddCompletion(c, xasprintf("%s%s", s, it->name + plen)); - } - } - if ((m = PyImport_ImportModule("pkgutil"))) { - if ((f = PyObject_GetAttrString(m, "iter_modules"))) { - if ((g = PyObject_CallFunctionObjArgs(f, 0))) { - if ((i = PyObject_GetIter(g))) { - while ((v = PyIter_Next(i))) { - if ((n = PyObject_GetAttrString(v, "name"))) { - if (((name = PyUnicode_AsUTF8AndSize(n, &namelen)) && - namelen >= plen && !memcasecmp(name, p, plen))) { - AddCompletion(c, xasprintf("%s%s", s, name + plen)); - } - Py_DECREF(n); - } - Py_DECREF(v); - } - Py_DECREF(i); - } - Py_DECREF(g); - } - Py_DECREF(f); - } - Py_DECREF(m); - } -} - -static void -CompleteDict(const char *b, const char *q, const char *p, - linenoiseCompletions *c, PyObject *o) -{ - const char *s; - PyObject *k, *v; - Py_ssize_t i, m; - for (i = 0; PyDict_Next(o, &i, &k, &v);) { - if ((v != Py_None && PyUnicode_Check(k) && - (s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !memcasecmp(s, p, q - p))) { - AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); - } - } -} - -static void -CompleteDir(const char *b, const char *q, const char *p, - linenoiseCompletions *c, PyObject *o) -{ - Py_ssize_t m; - const char *s; - PyObject *d, *i, *k; - if ((d = PyObject_Dir(o))) { - if ((i = PyObject_GetIter(d))) { - while ((k = PyIter_Next(i))) { - if (((s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !memcasecmp(s, p, q - p) && - !(q - p == 0 && m > 4 && - (s[0+0] == '_' && s[0+1] == '_' && - s[m-1] == '_' && s[m-2] == '_')))) { - AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); - } - Py_DECREF(k); - } - Py_DECREF(i); - } - Py_DECREF(d); - } -} - -static void -Complete(const char *p, linenoiseCompletions *c) -{ - PyObject *o, *t, *i; - const char *q, *s, *b; - if (startswith(p, "import ")) { - for (q = p + 7; *q; ++q) { - if (!isalnum(*q) && *q != '_') { - return; - } - } - CompleteModule(p, p + 7, c); - return; - } - for (b = p, p += strlen(p); p > b; --p) { - if (!isalnum(p[-1]) && p[-1] != '.' && p[-1] != '_') { - break; - } - } - o = PyModule_GetDict(PyImport_AddModule("__main__")); - if (!*(q = strchrnul(p, '.'))) { - CompleteDict(b, q, p, c, o); - CompleteDir(b, q, p, c, PyDict_GetItemString(o, "__builtins__")); - } else { - s = strndup(p, q - p); - if ((t = PyDict_GetItemString(o, s))) { - Py_INCREF(t); - } else { - o = PyDict_GetItemString(o, "__builtins__"); - if (PyObject_HasAttrString(o, s)) { - t = PyObject_GetAttrString(o, s); - } - } - while ((p = q + 1), (o = t)) { - if (*(q = strchrnul(p, '.'))) { - t = PyObject_GetAttrString(o, gc(strndup(p, q - p))); - Py_DECREF(o); - } else { - CompleteDir(b, q, p, c, o); - Py_DECREF(o); - break; - } - } - free(s); - } -} - -static void -TerminalCompletion(const char *p, linenoiseCompletions *c) -{ - Complete(p, c); - if (PyErr_Occurred()) { - PyErr_Clear(); - } -} - -static char * -TerminalHint(const char *p, const char **ansi1, const char **ansi2) -{ - char *h = 0; - linenoiseCompletions c = {0}; - TerminalCompletion(p, &c); - if (c.len == 1) h = strdup(c.cvec[0] + strlen(p)); - linenoiseFreeCompletions(&c); - return h; -} - -static char * -ReinterpretCommand(const char *line) -{ - size_t n; - n = strlen(line); - if (n && line[n - 1] == '?') { - return xstrcat("help(", gc(strndup(gc(line), n - 1)), ')'); - } else { - return line; - } -} - -static char * -TerminalReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) -{ - size_t n; - char *p, *q; - PyOS_sighandler_t saint; - saint = PyOS_setsig(SIGINT, OnKeyboardInterrupt); - p = linenoiseWithHistory(prompt, "python"); - PyOS_setsig(SIGINT, saint); - if (g_gotint) { - PyOS_setsig(SIGINT, saint); - g_gotint = 0; - q = 0; - } else if (p) { - p = ReinterpretCommand(p); - n = strlen(p); - q = PyMem_RawMalloc(n + 2); - strcpy(mempcpy(q, p, n), "\n"); - clearerr(sys_stdin); - } else if ((q = PyMem_RawMalloc(1))) { - *q = 0; - } - free(p); - return q; -} - -int -RunPythonModule(int argc, char **argv) -{ - char *launchargs[4]; - wchar_t **argv_copy; - /* We need a second copy, as Python might modify the first one. */ - wchar_t **argv_copy2; - int i, res; - char *oldloc; - - if (argc == 1 && weaken(kLaunchPythonModuleName)) { - launchargs[0] = argv[0]; - launchargs[1] = strdup("-m"); - launchargs[2] = strdup(kLaunchPythonModuleName); - launchargs[3] = 0; - argc = 3; - argv = launchargs; - } - - PyOS_ReadlineFunctionPointer = TerminalReadline; - linenoiseSetCompletionCallback(TerminalCompletion); - linenoiseSetHintsCallback(TerminalHint); - linenoiseSetFreeHintsCallback(free); - -#if IsModeDbg() - /* Force malloc() allocator to bootstrap Python */ - _PyMem_SetupAllocators("malloc"); -#endif - - argv_copy = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1)); - argv_copy2 = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1)); - if (!argv_copy || !argv_copy2) { - fprintf(stderr, "out of memory\n"); - return 1; - } - - /* 754 requires that FP exceptions run in "no stop" mode by default, - * and until C vendors implement C99's ways to control FP exceptions, - * Python requires non-stop mode. Alas, some platforms enable FP - * exceptions by default. Here we disable them. - */ -#ifdef __FreeBSD__ - fedisableexcept(FE_OVERFLOW); -#endif - - oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL)); - if (!oldloc) { - fprintf(stderr, "out of memory\n"); - return 1; - } - - setlocale(LC_ALL, ""); - for (i = 0; i < argc; i++) { - argv_copy[i] = Py_DecodeLocale(argv[i], NULL); - if (!argv_copy[i]) { - PyMem_RawFree(oldloc); - fprintf(stderr, "Fatal Python error: " - "unable to decode the command line argument #%i\n", - i + 1); - return 1; - } - argv_copy2[i] = argv_copy[i]; - } - argv_copy2[argc] = argv_copy[argc] = NULL; - - setlocale(LC_ALL, oldloc); - PyMem_RawFree(oldloc); - - res = Py_Main(argc, argv_copy); - -#if IsModeDbg() - /* Force again malloc() allocator to release memory blocks allocated - before Py_Main() */ - _PyMem_SetupAllocators("malloc"); -#endif - - for (i = 0; i < argc; i++) { - PyMem_RawFree(argv_copy2[i]); - } - PyMem_RawFree(argv_copy); - PyMem_RawFree(argv_copy2); - return res; -} +// clang-format off int main(int argc, char **argv) diff --git a/third_party/python/runpythonmodule.c b/third_party/python/runpythonmodule.c new file mode 100644 index 000000000..52f4e5f46 --- /dev/null +++ b/third_party/python/runpythonmodule.c @@ -0,0 +1,356 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Python 3 │ +│ https://docs.python.org/3/license.html │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#define PY_SSIZE_T_CLEAN +#include "libc/bits/bits.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/bits/weaken.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/ucontext.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sig.h" +#include "libc/time/time.h" +#include "libc/unicode/locale.h" +#include "libc/x/x.h" +#include "third_party/linenoise/linenoise.h" +#include "third_party/python/Include/abstract.h" +#include "third_party/python/Include/ceval.h" +#include "third_party/python/Include/dictobject.h" +#include "third_party/python/Include/fileutils.h" +#include "third_party/python/Include/funcobject.h" +#include "third_party/python/Include/import.h" +#include "third_party/python/Include/listobject.h" +#include "third_party/python/Include/moduleobject.h" +#include "third_party/python/Include/object.h" +#include "third_party/python/Include/pydebug.h" +#include "third_party/python/Include/pyerrors.h" +#include "third_party/python/Include/pylifecycle.h" +#include "third_party/python/Include/pymem.h" +#include "third_party/python/Include/pyport.h" +#include "third_party/python/Include/pythonrun.h" +#include "third_party/python/Include/unicodeobject.h" +#include "third_party/python/Include/yoink.h" +#include "third_party/xed/x86.h" +// clang-format off + +STATIC_STACK_SIZE(0x100000); + +STATIC_YOINK("__die"); +STATIC_YOINK("zip_uri_support"); + +PYTHON_YOINK("cosmo"); +PYTHON_YOINK("_locale"); +PYTHON_YOINK("_bootlocale"); +PYTHON_YOINK("encodings.aliases"); +PYTHON_YOINK("encodings.latin_1"); +PYTHON_YOINK("encodings.utf_8"); + +extern char kLaunchPythonModuleName[]; /* optionally generated by pyobj.com */ +const struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules; +struct _inittab *PyImport_Inittab = _PyImport_Inittab; +static int g_gotint; + +static void +OnKeyboardInterrupt(int sig) +{ + g_gotint = sig; +} + +static void +AddCompletion(linenoiseCompletions *c, char *s) +{ + char **p = c->cvec; + size_t n = c->len + 1; + if ((p = realloc(p, n * sizeof(*p)))) { + p[n - 1] = s; + c->cvec = p; + c->len = n; + } +} + +static void +CompleteModule(const char *s, const char *p, linenoiseCompletions *c) +{ + const char *name; + struct _inittab *it; + Py_ssize_t plen, namelen; + PyObject *m, *f, *g, *i, *v, *n; + plen = strlen(p); + for (it = PyImport_Inittab; it->name; ++it) { + if (startswithi(it->name, p)) { + AddCompletion(c, xasprintf("%s%s", s, it->name + plen)); + } + } + if ((m = PyImport_ImportModule("pkgutil"))) { + if ((f = PyObject_GetAttrString(m, "iter_modules"))) { + if ((g = PyObject_CallFunctionObjArgs(f, 0))) { + if ((i = PyObject_GetIter(g))) { + while ((v = PyIter_Next(i))) { + if ((n = PyObject_GetAttrString(v, "name"))) { + if (((name = PyUnicode_AsUTF8AndSize(n, &namelen)) && + namelen >= plen && !memcasecmp(name, p, plen))) { + AddCompletion(c, xasprintf("%s%s", s, name + plen)); + } + Py_DECREF(n); + } + Py_DECREF(v); + } + Py_DECREF(i); + } + Py_DECREF(g); + } + Py_DECREF(f); + } + Py_DECREF(m); + } +} + +static void +CompleteDict(const char *b, const char *q, const char *p, + linenoiseCompletions *c, PyObject *o) +{ + const char *s; + PyObject *k, *v; + Py_ssize_t i, m; + for (i = 0; PyDict_Next(o, &i, &k, &v);) { + if ((v != Py_None && PyUnicode_Check(k) && + (s = PyUnicode_AsUTF8AndSize(k, &m)) && + m >= q - p && !memcasecmp(s, p, q - p))) { + AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); + } + } +} + +static void +CompleteDir(const char *b, const char *q, const char *p, + linenoiseCompletions *c, PyObject *o) +{ + Py_ssize_t m; + const char *s; + PyObject *d, *i, *k; + if ((d = PyObject_Dir(o))) { + if ((i = PyObject_GetIter(d))) { + while ((k = PyIter_Next(i))) { + if (((s = PyUnicode_AsUTF8AndSize(k, &m)) && + m >= q - p && !memcasecmp(s, p, q - p) && + !(q - p == 0 && m > 4 && + (s[0+0] == '_' && s[0+1] == '_' && + s[m-1] == '_' && s[m-2] == '_')))) { + AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); + } + Py_DECREF(k); + } + Py_DECREF(i); + } + Py_DECREF(d); + } +} + +static void +Complete(const char *p, linenoiseCompletions *c) +{ + PyObject *o, *t, *i; + const char *q, *s, *b; + if (startswith(p, "import ")) { + for (q = p + 7; *q; ++q) { + if (!isalnum(*q) && *q != '_') { + return; + } + } + CompleteModule(p, p + 7, c); + return; + } + for (b = p, p += strlen(p); p > b; --p) { + if (!isalnum(p[-1]) && p[-1] != '.' && p[-1] != '_') { + break; + } + } + o = PyModule_GetDict(PyImport_AddModule("__main__")); + if (!*(q = strchrnul(p, '.'))) { + CompleteDict(b, q, p, c, o); + CompleteDir(b, q, p, c, PyDict_GetItemString(o, "__builtins__")); + } else { + s = strndup(p, q - p); + if ((t = PyDict_GetItemString(o, s))) { + Py_INCREF(t); + } else { + o = PyDict_GetItemString(o, "__builtins__"); + if (PyObject_HasAttrString(o, s)) { + t = PyObject_GetAttrString(o, s); + } + } + while ((p = q + 1), (o = t)) { + if (*(q = strchrnul(p, '.'))) { + t = PyObject_GetAttrString(o, gc(strndup(p, q - p))); + Py_DECREF(o); + } else { + CompleteDir(b, q, p, c, o); + Py_DECREF(o); + break; + } + } + free(s); + } +} + +static void +TerminalCompletion(const char *p, linenoiseCompletions *c) +{ + Complete(p, c); + if (PyErr_Occurred()) { + PyErr_Clear(); + } +} + +static char * +TerminalHint(const char *p, const char **ansi1, const char **ansi2) +{ + char *h = 0; + linenoiseCompletions c = {0}; + TerminalCompletion(p, &c); + if (c.len == 1) h = strdup(c.cvec[0] + strlen(p)); + linenoiseFreeCompletions(&c); + return h; +} + +static char * +ReinterpretCommand(const char *line) +{ + size_t n; + n = strlen(line); + if (n && line[n - 1] == '?') { + return xstrcat("help(", gc(strndup(gc(line), n - 1)), ')'); + } else { + return line; + } +} + +static char * +TerminalReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) +{ + size_t n; + char *p, *q; + PyOS_sighandler_t saint; + saint = PyOS_setsig(SIGINT, OnKeyboardInterrupt); + p = linenoiseWithHistory(prompt, "python"); + PyOS_setsig(SIGINT, saint); + if (g_gotint) { + PyOS_setsig(SIGINT, saint); + g_gotint = 0; + q = 0; + } else if (p) { + p = ReinterpretCommand(p); + n = strlen(p); + q = PyMem_RawMalloc(n + 2); + strcpy(mempcpy(q, p, n), "\n"); + clearerr(sys_stdin); + } else if ((q = PyMem_RawMalloc(1))) { + *q = 0; + } + free(p); + return q; +} + +int +RunPythonModule(int argc, char **argv) +{ + char *launchargs[4]; + wchar_t **argv_copy; + /* We need a second copy, as Python might modify the first one. */ + wchar_t **argv_copy2; + int i, res; + char *oldloc; + + if (argc == 1 && weaken(kLaunchPythonModuleName)) { + launchargs[0] = argv[0]; + launchargs[1] = strdup("-m"); + launchargs[2] = strdup(kLaunchPythonModuleName); + launchargs[3] = 0; + argc = 3; + argv = launchargs; + } + + PyOS_ReadlineFunctionPointer = TerminalReadline; + linenoiseSetCompletionCallback(TerminalCompletion); + linenoiseSetHintsCallback(TerminalHint); + linenoiseSetFreeHintsCallback(free); + +#if IsModeDbg() + /* Force malloc() allocator to bootstrap Python */ + _PyMem_SetupAllocators("malloc"); +#endif + + argv_copy = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1)); + argv_copy2 = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1)); + if (!argv_copy || !argv_copy2) { + fprintf(stderr, "out of memory\n"); + return 1; + } + + /* 754 requires that FP exceptions run in "no stop" mode by default, + * and until C vendors implement C99's ways to control FP exceptions, + * Python requires non-stop mode. Alas, some platforms enable FP + * exceptions by default. Here we disable them. + */ +#ifdef __FreeBSD__ + fedisableexcept(FE_OVERFLOW); +#endif + + oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL)); + if (!oldloc) { + fprintf(stderr, "out of memory\n"); + return 1; + } + + setlocale(LC_ALL, ""); + for (i = 0; i < argc; i++) { + argv_copy[i] = Py_DecodeLocale(argv[i], NULL); + if (!argv_copy[i]) { + PyMem_RawFree(oldloc); + fprintf(stderr, "Fatal Python error: " + "unable to decode the command line argument #%i\n", + i + 1); + return 1; + } + argv_copy2[i] = argv_copy[i]; + } + argv_copy2[argc] = argv_copy[argc] = NULL; + + setlocale(LC_ALL, oldloc); + PyMem_RawFree(oldloc); + + res = Py_Main(argc, argv_copy); + +#if IsModeDbg() + /* Force again malloc() allocator to release memory blocks allocated + before Py_Main() */ + _PyMem_SetupAllocators("malloc"); +#endif + + for (i = 0; i < argc; i++) { + PyMem_RawFree(argv_copy2[i]); + } + PyMem_RawFree(argv_copy); + PyMem_RawFree(argv_copy2); + return res; +} diff --git a/third_party/python/runpythonmodule.h b/third_party/python/runpythonmodule.h new file mode 100644 index 000000000..2aa150780 --- /dev/null +++ b/third_party/python/runpythonmodule.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_PYTHON_RUNPYTHONMODULE_H_ +#define COSMOPOLITAN_THIRD_PARTY_PYTHON_RUNPYTHONMODULE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int RunPythonModule(int, char **); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_PYTHON_RUNPYTHONMODULE_H_ */ diff --git a/tool/args/args.c b/tool/args/args.c index 4bed2a002..aec1c73ff 100644 --- a/tool/args/args.c +++ b/tool/args/args.c @@ -29,7 +29,7 @@ STATIC_YOINK("zip_uri_support"); static struct ZipArgs { - bool registered; + bool initialized; bool loaded; int oldargc; char *data; @@ -76,15 +76,15 @@ int LoadZipArgsImpl(int *argc, char ***argv, char *data) { start = 0; } if (founddots || *argc <= 1) { - if (!g_zipargs.registered) { + if (!g_zipargs.initialized) { atexit(FreeZipArgs); - g_zipargs.registered = true; + g_zipargs.oldargc = __argc; + g_zipargs.oldargv = __argv; + g_zipargs.initialized = true; } g_zipargs.loaded = true; g_zipargs.data = data; g_zipargs.args = args; - g_zipargs.oldargc = *argc; - g_zipargs.oldargv = *argv; *argc = n; *argv = args; __argc = n; diff --git a/tool/emacs/cosmo-c-keywords.el b/tool/emacs/cosmo-c-keywords.el index 441b7c977..11ee51b9f 100644 --- a/tool/emacs/cosmo-c-keywords.el +++ b/tool/emacs/cosmo-c-keywords.el @@ -99,7 +99,8 @@ "_Vector_size")) (gnu - '("__inline" + '("__extension__" + "__inline" "__thread" "__alignof" "__typeof" diff --git a/tool/net/counters.inc b/tool/net/counters.inc index 96b3579a0..4f55e94f8 100644 --- a/tool/net/counters.inc +++ b/tool/net/counters.inc @@ -1,4 +1,5 @@ C(accepterrors) +C(acceptflakes) C(acceptinterrupts) C(acceptresets) C(badlengths) @@ -74,6 +75,7 @@ C(sslunknownca) C(sslunknowncert) C(sslupgrades) C(sslverifyfailed) +C(stackuse) C(statfails) C(staticrequests) C(stats) diff --git a/tool/net/help.txt b/tool/net/help.txt index 4cef7ae1a..15814bd41 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -54,6 +54,7 @@ FLAGS -Z log worker system calls -f log worker function calls -B only use stronger cryptography + -X disable ssl server and client support -s increase silence [repeatable] -v increase verbosity [repeatable] -V increase ssl verbosity [repeatable] @@ -1980,13 +1981,15 @@ UNIX MODULE Makes directories. + Unlike mkdir() this convenience wrapper will automatically create + parent parent directories as needed. If the directory already exists + then, unlike mkdir() which returns EEXIST, the makedirs() function + will return success. + `path` is the path of the directory you wish to create. `mode` is octal permission bits, e.g. `0755`. - Unlike mkdir() this convenience wrapper will automatically create - parent parent directories as needed. - unix.chdir(path:str) ├─→ true └─→ nil, unix.Errno diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 45a58f589..d5e3ce614 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/scale/cdecimate2xuint8x8.h" +#include "libc/bits/atomic.h" #include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/popcnt.h" @@ -204,34 +205,26 @@ STATIC_YOINK("zip_uri_support"); #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) -#define TRACE_BEGIN \ - do { \ - if (!IsTiny()) { \ - if (funtrace) { \ - __atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \ - } \ - if (systrace) { \ - __atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED); \ - } \ - } \ +#define TRACE_BEGIN \ + do { \ + if (!IsTiny()) { \ + if (funtrace) ++__ftrace; \ + if (systrace) ++__strace; \ + } \ } while (0) -#define TRACE_END \ - do { \ - if (!IsTiny()) { \ - if (funtrace) { \ - __atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \ - } \ - if (systrace) { \ - __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); \ - } \ - } \ +#define TRACE_END \ + do { \ + if (!IsTiny()) { \ + if (funtrace) --__ftrace; \ + if (systrace) --__strace; \ + } \ } while (0) -// letters not used: EIJNOQXYnoqwxy +// letters not used: EIJNOQYnoqwxy // digits not used: 0123456789 // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ -#define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:W:c:e:l:p:r:t:" +#define GETOPTS "BSVXZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:W:c:e:l:p:r:t:" extern unsigned long long __kbirth; @@ -403,6 +396,7 @@ static bool branded; static bool funtrace; static bool systrace; static bool meltdown; +static bool unsecure; static bool printport; static bool daemonize; static bool logrusage; @@ -425,7 +419,6 @@ static bool sslclientverify; static bool connectionclose; static bool hasonworkerstop; static bool isexitingworker; -static bool terminatemonitor; static bool hasonworkerstart; static bool leakcrashreports; static bool hasonhttprequest; @@ -437,6 +430,7 @@ static bool loggednetworkorigin; static bool ishandlingconnection; static bool hasonclientconnection; static bool evadedragnetsurveillance; +static _Atomic(bool) terminatemonitor; static int zfd; static int frags; @@ -1213,7 +1207,7 @@ static void CallSimpleHookIfDefined(const char *s) { static void ReportWorkerExit(int pid, int ws) { int workers; - workers = __atomic_sub_fetch(&shared->workers, 1, __ATOMIC_SEQ_CST); + workers = atomic_fetch_sub(&shared->workers, 1) - 1; if (WIFEXITED(ws)) { if (WEXITSTATUS(ws)) { LockInc(&shared->c.failedchildren); @@ -3696,6 +3690,7 @@ static int LuaStoreAsset(lua_State *L) { static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) { #ifndef UNSECURE + if (unsecure) return; CHECK_EQ(0, mbedtls_ctr_drbg_reseed(r, (void *)s, strlen(s))); #endif } @@ -3848,7 +3843,8 @@ static int LuaFetch(lua_State *L) { usessl = false; if (url.scheme.n) { #ifndef UNSECURE - if (url.scheme.n == 5 && !memcasecmp(url.scheme.p, "https", 5)) { + if (!unsecure && url.scheme.n == 5 && + !memcasecmp(url.scheme.p, "https", 5)) { usessl = true; } else #endif @@ -3858,14 +3854,20 @@ static int LuaFetch(lua_State *L) { } } +#ifndef UNSECURE if (usessl && !sslinitialized) TlsInit(); +#endif 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)); +#ifndef UNSECURE + } else if (usessl) { + port = "443"; +#endif } else { - port = usessl ? "443" : "80"; + port = "80"; } } else { ip = servers.n ? ntohl(servers.p[0].addr.sin_addr.s_addr) : INADDR_LOOPBACK; @@ -3932,6 +3934,7 @@ static int LuaFetch(lua_State *L) { unreachable; } +#ifndef UNSECURE if (usessl) { if (sslcliused) { mbedtls_ssl_session_reset(&sslcli); @@ -3966,11 +3969,13 @@ static int LuaFetch(lua_State *L) { mbedtls_ssl_get_ciphersuite(&sslcli), mbedtls_ssl_get_version(&sslcli)); } +#endif /* UNSECURE */ /* * Send HTTP Message. */ DEBUGF("(ftch) client sending %s request", method); +#ifndef UNSECURE if (usessl) { ret = mbedtls_ssl_write(&sslcli, request, requestlen); if (ret != requestlen) { @@ -3979,7 +3984,9 @@ static int LuaFetch(lua_State *L) { LuaThrowTlsError(L, "write", ret); unreachable; } - } else if (WRITE(sock, request, requestlen) != requestlen) { + } else +#endif + if (WRITE(sock, request, requestlen) != requestlen) { close(sock); luaL_error(L, "write error: %s", strerror(errno)); unreachable; @@ -4000,6 +4007,7 @@ static int LuaFetch(lua_State *L) { inbuf.p = realloc(inbuf.p, inbuf.c); } NOISEF("(ftch) client reading"); +#ifndef UNSECURE if (usessl) { if ((rc = mbedtls_ssl_read(&sslcli, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) < 0) { @@ -4013,7 +4021,9 @@ static int LuaFetch(lua_State *L) { unreachable; } } - } else if ((rc = READ(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) { + } else +#endif + if ((rc = READ(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) { close(sock); free(inbuf.p); DestroyHttpMessage(&msg); @@ -4163,6 +4173,7 @@ TransportError: close(sock); luaL_error(L, "transport error"); unreachable; +#ifndef UNSECURE VerifyFailed: LockInc(&shared->c.sslverifyfailed); close(sock); @@ -4170,6 +4181,7 @@ VerifyFailed: L, gc(DescribeSslVerifyFailure(sslcli.session_negotiate->verify_result)), ret); unreachable; +#endif #undef ssl } @@ -4828,8 +4840,11 @@ static int LuaEvadeDragnetSurveillance(lua_State *L) { static int LuaProgramSslCompression(lua_State *L) { #ifndef UNSECURE - OnlyCallFromInitLua(L, "ProgramSslCompression"); - conf.disable_compression = confcli.disable_compression = !lua_toboolean(L, 1); + if (!unsecure) { + OnlyCallFromInitLua(L, "ProgramSslCompression"); + conf.disable_compression = confcli.disable_compression = + !lua_toboolean(L, 1); + } #endif return 0; } @@ -5071,6 +5086,7 @@ static const luaL_Reg kLuaFuncs[] = { {"EscapePath", LuaEscapePath}, // {"EscapeSegment", LuaEscapeSegment}, // {"EscapeUser", LuaEscapeUser}, // + {"Fetch", LuaFetch}, // {"FormatHttpDateTime", LuaFormatHttpDateTime}, // {"FormatIp", LuaFormatIp}, // {"GetAssetComment", LuaGetAssetComment}, // @@ -5194,7 +5210,6 @@ static const luaL_Reg kLuaFuncs[] = { {"hex", LuaHex}, // {"oct", LuaOct}, // #ifndef UNSECURE - {"Fetch", LuaFetch}, // {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // {"GetSslIdentity", LuaGetSslIdentity}, // {"ProgramSslCiphersuite", LuaProgramSslCiphersuite}, // @@ -5541,6 +5556,7 @@ static char *HandleMapFailed(struct Asset *a, int fd) { } static void LogAcceptError(const char *s) { + LockInc(&shared->c.accepterrors); WARNF("(srvr) %s accept error: %s", DescribeServer(), s); } @@ -6270,14 +6286,16 @@ static void HandleMessages(void) { #ifndef UNSECURE if (!once) { once = true; - if (IsSsl(inbuf.p[0])) { - if (TlsSetup()) { - continue; + if (!unsecure) { + if (IsSsl(inbuf.p[0])) { + if (TlsSetup()) { + continue; + } else { + return; + } } else { - return; + WipeServingKeys(); } - } else { - WipeServingKeys(); } } #endif @@ -6300,15 +6318,15 @@ static void HandleMessages(void) { LockInc(&shared->c.readtimeouts); if (amtread) SendTimeout(); NotifyClose(); - LogClose("timeout"); + LogClose("readtimeout"); return; } else if (errno == ECONNRESET) { LockInc(&shared->c.readresets); - LogClose("reset"); + LogClose("readreset"); return; } else { LockInc(&shared->c.readerrors); - WARNF("(clnt) %s read error: %m", DescribeClient()); + WARNF("(clnt) %s readerror: %m", DescribeClient()); return; } if (killed || (terminated && !amtread) || @@ -6339,7 +6357,7 @@ static void HandleMessages(void) { } else { CHECK_LT(msgsize, amtread); LockInc(&shared->c.pipelinedrequests); - DEBUGF("(stat) %,ld pipelined bytes", amtread - msgsize); + DEBUGF("(stat) %,ld pipelinedrequest bytes", amtread - msgsize); memmove(inbuf.p, inbuf.p + msgsize, amtread - msgsize); amtread -= msgsize; if (killed) { @@ -6488,7 +6506,7 @@ static int MemoryMonitor(void *arg) { long i, j, k, n, x, y, pi, gen, pages; int rc, id, color, color2, workers; _spinlock(&memmonalive); - __atomic_load(&shared->workers, &id, __ATOMIC_SEQ_CST); + id = atomic_load_explicit(&shared->workers, memory_order_relaxed); DEBUGF("(memv) started for pid %d on tid %d", getpid(), gettid()); sigemptyset(&ss); @@ -6521,9 +6539,8 @@ static int MemoryMonitor(void *arg) { if (tty != -1) { for (gen = 0, mi = 0, b = 0;;) { - __atomic_load(&terminatemonitor, &done, __ATOMIC_SEQ_CST); - if (done) break; - __atomic_load(&shared->workers, &workers, __ATOMIC_SEQ_CST); + if (terminatemonitor) break; + workers = atomic_load_explicit(&shared->workers, memory_order_relaxed); if (id) id = MAX(1, MIN(id, workers)); if (!id && workers) { usleep(50000); @@ -6531,7 +6548,7 @@ static int MemoryMonitor(void *arg) { } ++gen; - __atomic_load(&_mmi.i, &intervals, __ATOMIC_SEQ_CST); + intervals = atomic_load_explicit(&_mmi.i, memory_order_relaxed); if ((mi2 = realloc(mi, (intervals += 3) * sizeof(*mi)))) { mi = mi2; mi[0].x = (intptr_t)_base >> 16; @@ -6713,35 +6730,37 @@ static int HandleConnection(size_t i) { LockInc(&shared->c.acceptinterrupts); } else if (errno == ENFILE) { LockInc(&shared->c.enfiles); - LogAcceptError("too many open files"); + LogAcceptError("enfile: too many open files"); meltdown = true; } else if (errno == EMFILE) { LockInc(&shared->c.emfiles); - LogAcceptError("ran out of open file quota"); + LogAcceptError("emfile: ran out of open file quota"); meltdown = true; } else if (errno == ENOMEM) { LockInc(&shared->c.enomems); - LogAcceptError("ran out of memory"); + LogAcceptError("enomem: ran out of memory"); meltdown = true; } else if (errno == ENOBUFS) { LockInc(&shared->c.enobufs); - LogAcceptError("ran out of buffer"); + LogAcceptError("enobuf: ran out of buffer"); meltdown = true; } else if (errno == ENONET) { LockInc(&shared->c.enonets); - LogAcceptError("network gone"); + LogAcceptError("enonet: network gone"); polls[i].fd = -polls[i].fd; } else if (errno == ENETDOWN) { LockInc(&shared->c.enetdowns); - LogAcceptError("network down"); + LogAcceptError("enetdown: network down"); polls[i].fd = -polls[i].fd; } else if (errno == ECONNABORTED) { + LockInc(&shared->c.accepterrors); LockInc(&shared->c.acceptresets); WARNF("(srvr) %S accept error: %s", DescribeServer(), - "connection reset before accept"); + "acceptreset: connection reset before accept"); } else if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) { LockInc(&shared->c.accepterrors); + LockInc(&shared->c.acceptflakes); WARNF("(srvr) accept error: %s ephemeral accept error: %m", DescribeServer()); } else { @@ -6753,6 +6772,7 @@ static int HandleConnection(size_t i) { } static void RestoreApe(void) { + int ft; char *p; size_t n; struct Asset *a; @@ -6764,9 +6784,15 @@ static void RestoreApe(void) { if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); + ft = __ftrace; if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) { WARNF("(srvr) can't restore .ape"); } + if (ft > 0) { + __ftrace = 0; + ftrace_install(); + __ftrace = ft; + } free(p); } else { DEBUGF("(srvr) /.ape not found"); @@ -7036,6 +7062,7 @@ static void SigInit(void) { static void TlsInit(void) { #ifndef UNSECURE int suite; + if (unsecure) return; if (!sslinitialized) { InitializeRng(&rng); @@ -7095,6 +7122,7 @@ static void TlsInit(void) { static void TlsDestroy(void) { #ifndef UNSECURE + if (unsecure) return; mbedtls_ssl_free(&ssl); mbedtls_ssl_free(&sslcli); mbedtls_ctr_drbg_free(&rng); @@ -7139,6 +7167,7 @@ static void GetOpts(int argc, char *argv[]) { CASE('S', ++sandboxed); CASE('v', ++__log_level); CASE('s', --__log_level); + CASE('X', unsecure = true); CASE('Z', systrace = true); CASE('b', logbodies = true); CASE('z', printport = true); diff --git a/tool/plinko/lib/plinko.c b/tool/plinko/lib/plinko.c index 5f6e6379c..5035420b3 100644 --- a/tool/plinko/lib/plinko.c +++ b/tool/plinko/lib/plinko.c @@ -674,9 +674,9 @@ struct T DispatchTrace(dword ea, dword tm, dword r, dword p1, dword p2, struct T DispatchFtrace(dword ea, dword tm, dword r, dword p1, dword p2, dword d) { ftrace_install(); - ++g_ftrace; + ++__ftrace; ea = MAKE(recurse(MAKE(Cadr(LO(ea)), HI(ea)), p1, p2), 0); - --g_ftrace; + --__ftrace; return Ret(ea, tm, r); } diff --git a/tool/plinko/lib/printf.c b/tool/plinko/lib/printf.c index 839ef7216..43fceab34 100644 --- a/tool/plinko/lib/printf.c +++ b/tool/plinko/lib/printf.c @@ -80,7 +80,7 @@ int Vfnprintf(const char *f, va_list va, int fd, int n) { int b, c, i, x, y, si, prec, cols, sign; gotr = false; t = rdtsc(); - --g_ftrace; + --__ftrace; --__strace; ++recursive; for (ansi = 0;;) { @@ -290,7 +290,7 @@ int Vfnprintf(const char *f, va_list va, int fd, int n) { } } --recursive; - ++g_ftrace; + ++__ftrace; ++__strace; if (!recursive) { u = rdtsc(); diff --git a/tool/plinko/lib/read.c b/tool/plinko/lib/read.c index f35c06a6f..ac0a7b76c 100644 --- a/tool/plinko/lib/read.c +++ b/tool/plinko/lib/read.c @@ -280,10 +280,10 @@ static int Read1(int fd) { int Read(int fd) { int r; - --g_ftrace; + --__ftrace; --__strace; r = Read1(fd); - ++g_ftrace; + ++__ftrace; ++__strace; return r; } diff --git a/tool/plinko/plinko.c b/tool/plinko/plinko.c index eaa47a2ce..90158369e 100644 --- a/tool/plinko/plinko.c +++ b/tool/plinko/plinko.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/kprintf.h" #include "libc/log/log.h" +#include "libc/stdio/stdio.h" #include "tool/plinko/lib/plinko.h" STATIC_YOINK("__zipos_get"); diff --git a/libc/calls/getfdhandleactual.greg.c b/tool/viz/echoctl.c similarity index 86% rename from libc/calls/getfdhandleactual.greg.c rename to tool/viz/echoctl.c index b0f797b12..281909034 100644 --- a/libc/calls/getfdhandleactual.greg.c +++ b/tool/viz/echoctl.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/sock/ntstdin.internal.h" +#include "libc/calls/struct/termios.h" +#include "libc/calls/termios.h" +#include "libc/sysv/consts/termios.h" -int64_t __getfdhandleactual(int fd) { - if (g_fds.p[fd].worker) { - return g_fds.p[fd].worker->reader; - } else { - return g_fds.p[fd].handle; - } +int main(int argc, char *argv[]) { + struct termios t; + if (tcgetattr(0, &t) == -1) return 1; + t.c_lflag ^= ECHOCTL; + if (tcsetattr(0, TCSANOW, &t) == -1) return 2; } diff --git a/libc/calls/sched_yield-nt.c b/tool/viz/vdsodump.c similarity index 53% rename from libc/calls/sched_yield-nt.c rename to tool/viz/vdsodump.c index fc6fbb0fb..f5042a241 100644 --- a/libc/calls/sched_yield-nt.c +++ b/tool/viz/vdsodump.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 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 │ @@ -16,19 +16,66 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/nt/enum/status.h" -#include "libc/nt/ntdll.h" -#include "libc/nt/synchronization.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/ucontext.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "third_party/xed/x86.h" + +#define OUTPATH "vdso.elf" + +volatile bool finished; + +void OnSegmentationFault(int sig, siginfo_t *si, ucontext_t *ctx) { + struct XedDecodedInst xedd; + xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); + xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); + ctx->uc_mcontext.rip += xedd.length; + finished = true; +} + +int main(int argc, char *argv[]) { + FILE *f; + int byte; + volatile unsigned char *vdso, *p; + + vdso = (unsigned char *)getauxval(AT_SYSINFO_EHDR); + if (vdso) { + fprintf(stderr, "vdso found at address %p\n", vdso); + } else { + fprintf(stderr, "error: AT_SYSINFO_EHDR was not in auxiliary values\n"); + return 1; + } + + f = fopen(OUTPATH, "wb"); + if (!f) { + fprintf(stderr, "error: fopen(%`'s) failed\n", OUTPATH); + return 1; + } + + struct sigaction sa = { + .sa_sigaction = OnSegmentationFault, + .sa_flags = SA_SIGINFO, + }; + sigaction(SIGSEGV, &sa, 0); + sigaction(SIGBUS, &sa, 0); + + p = vdso; + for (;;) { + byte = *p++; + if (!finished) { + fputc(byte, f); + } else { + break; + } + } + + fclose(f); + fprintf(stderr, "%zu bytes dumped to %s\n", p - vdso, OUTPATH); -textwindows int sys_sched_yield_nt(void) { - // A value of zero, together with the bAlertable parameter set to - // FALSE, causes the thread to relinquish the remainder of its time - // slice to any other thread that is ready to run, if there are no - // pending user APCs on the calling thread. If there are no other - // threads ready to run and no user APCs are queued, the function - // returns immediately, and the thread continues execution. - // ──Quoth MSDN - SleepEx(0, false); return 0; } From 6e52cba37a6860ee2ff9b521b45f5da491bddd07 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 19 May 2022 00:34:15 -0700 Subject: [PATCH 17/40] Fix stdio regression This change fixes a nasty regression caused by 80b211e314512fc8dd77159ac162bcc13cd89c08 which deadlocked. This change also causes MbedTLS to prefer the ChaCha ciphersuite on older CPUs that don't have AES hardware instructions. --- libc/stdio/unlocked/getchar_unlocked.S | 2 +- libc/stdio/unlocked/getwchar_unlocked.S | 2 +- libc/stdio/unlocked/putchar_unlocked.S | 2 +- libc/stdio/unlocked/putwchar_unlocked.S | 2 +- libc/sysv/{errno.greg.c => errno.c} | 17 +-- libc/sysv/errno_location.greg.c | 31 +++++ libc/sysv/sysv.mk | 9 +- test/libc/runtime/munmap_test.c | 25 +++- third_party/mbedtls/ssl_ciphersuites.c | 155 ++++++++++++++++++++++-- third_party/mbedtls/ssl_tls.c | 1 + third_party/python/Objects/obmalloc.c | 10 +- tool/build/runitd.c | 2 +- tool/net/redbean.c | 4 + tool/viz/cpuid.c | 21 ++-- 14 files changed, 232 insertions(+), 51 deletions(-) rename libc/sysv/{errno.greg.c => errno.c} (88%) create mode 100644 libc/sysv/errno_location.greg.c diff --git a/libc/stdio/unlocked/getchar_unlocked.S b/libc/stdio/unlocked/getchar_unlocked.S index 5bc14faaa..532bc1412 100644 --- a/libc/stdio/unlocked/getchar_unlocked.S +++ b/libc/stdio/unlocked/getchar_unlocked.S @@ -23,7 +23,7 @@ // @return byte in range 0..255, or -1 w/ errno // @see fgetc_unlocked() getchar: - lea stdin(%rip),%rdi + mov stdin(%rip),%rdi mov %rdi,%r11 ezlea fgetc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/getwchar_unlocked.S b/libc/stdio/unlocked/getwchar_unlocked.S index f76f34285..a4d1bf6ea 100644 --- a/libc/stdio/unlocked/getwchar_unlocked.S +++ b/libc/stdio/unlocked/getwchar_unlocked.S @@ -23,7 +23,7 @@ // @return wide character or -1 on EOF or error // @see fgetwc_unlocked() getwchar: - lea stdin(%rip),%rdi + mov stdin(%rip),%rdi mov %rdi,%r11 ezlea fgetwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/putchar_unlocked.S b/libc/stdio/unlocked/putchar_unlocked.S index b1d55fab7..77dfa8cba 100644 --- a/libc/stdio/unlocked/putchar_unlocked.S +++ b/libc/stdio/unlocked/putchar_unlocked.S @@ -24,7 +24,7 @@ // @return c (as unsigned char) if written or -1 w/ errno // @see fputc_unlocked() putchar: - lea stdout(%rip),%rsi + mov stdout(%rip),%rsi mov %rsi,%r11 ezlea fputc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/putwchar_unlocked.S b/libc/stdio/unlocked/putwchar_unlocked.S index a24d6cd02..5232ae81b 100644 --- a/libc/stdio/unlocked/putwchar_unlocked.S +++ b/libc/stdio/unlocked/putwchar_unlocked.S @@ -24,7 +24,7 @@ // @return wc if written or -1 w/ errno // @see fputwc_unlocked() putwchar: - lea stdout(%rip),%rsi + mov stdout(%rip),%rsi mov %rsi,%r11 ezlea fputwc_unlocked,ax jmp stdio_unlock diff --git a/libc/sysv/errno.greg.c b/libc/sysv/errno.c similarity index 88% rename from libc/sysv/errno.greg.c rename to libc/sysv/errno.c index babf13461..267e1e48a 100644 --- a/libc/sysv/errno.greg.c +++ b/libc/sysv/errno.c @@ -17,7 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/nexgen32e/threaded.h" + +asm(".weak\t__asan_init"); +asm(".weak\t__asan_register_globals"); +asm(".weak\t__asan_unregister_globals"); +asm(".weak\t__asan_version_mismatch_check_v8"); /** * Global variable for last error. @@ -32,14 +36,3 @@ * @see __errno_location() stable abi */ errno_t __errno; - -/** - * Returns address of errno variable. - * - * @see __initialize_tls() - * @see __install_tls() - */ -privileged nocallersavedregisters errno_t *(__errno_location)(void) { - if (!__tls_enabled) return &__errno; - return (errno_t *)(__get_tls() + 0x3c); -} diff --git a/libc/sysv/errno_location.greg.c b/libc/sysv/errno_location.greg.c new file mode 100644 index 000000000..d196b5ae7 --- /dev/null +++ b/libc/sysv/errno_location.greg.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 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/errno.h" +#include "libc/nexgen32e/threaded.h" + +/** + * Returns address of errno variable. + * + * @see __initialize_tls() + * @see __install_tls() + */ +privileged nocallersavedregisters errno_t *(__errno_location)(void) { + if (!__tls_enabled) return &__errno; + return (errno_t *)(__get_tls() + 0x3c); +} diff --git a/libc/sysv/sysv.mk b/libc/sysv/sysv.mk index 65062f721..9bade5fc2 100644 --- a/libc/sysv/sysv.mk +++ b/libc/sysv/sysv.mk @@ -37,7 +37,8 @@ LIBC_SYSV_A_FILES := \ libc/sysv/restorert.S \ libc/sysv/syscall.S \ libc/sysv/systemfive.S \ - libc/sysv/errno.greg.c \ + libc/sysv/errno_location.greg.c \ + libc/sysv/errno.c \ libc/sysv/strace.greg.c \ libc/sysv/describeos.greg.c \ $(wildcard libc/sysv/consts/*) \ @@ -75,12 +76,6 @@ o/libc/sysv/consts/syscon.internal.inc: \ libc/macros-cpp.internal.inc \ libc/macros.internal.inc -# we can't use asan and ubsan because: -# we're higher in the topological order of things -o/$(MODE)/libc/sysv/errno.greg.o: \ - OVERRIDE_CFLAGS += \ - -fno-sanitize=all - #─────────────────────────────────────────────────────────────────────────────── LIBC_SYSV_CALLS = \ diff --git a/test/libc/runtime/munmap_test.c b/test/libc/runtime/munmap_test.c index 9c2998e68..47b230356 100644 --- a/test/libc/runtime/munmap_test.c +++ b/test/libc/runtime/munmap_test.c @@ -31,7 +31,7 @@ #include "libc/testlib/testlib.h" #include "third_party/xed/x86.h" -int gotsignal; +volatile int gotsignal; char testlib_enable_tmp_setup_teardown; void ContinueOnError(int sig, siginfo_t *si, ucontext_t *ctx) { @@ -207,7 +207,7 @@ TEST(munmap, tinyFile_roundupUnmapSize) { ASSERT_SYS(0, 0, close(3)); EXPECT_TRUE(MemoryExists(p)); // some kernels/versions support this, some don't - // EXPECT_FALSE(MemoryExists(p + 5)); + EXPECT_FALSE(MemoryExists(p + PAGESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_FALSE(MemoryExists(p)); EXPECT_FALSE(MemoryExists(p + 5)); @@ -230,3 +230,24 @@ TEST(munmap, tinyFile_preciseUnmapSize) { EXPECT_SYS(0, 0, munmap(q, 5)); EXPECT_FALSE(MemoryExists(q)); } + +// clang-format off +TEST(munmap, tinyFile_mapThriceUnmapOnce) { + char *p; + ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS (0, 5, write(3, "hello", 5)); + ASSERT_NE(MAP_FAILED, (p=mmap(0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))); + ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE, 3, 0)); + ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE, 3, 0)); + ASSERT_SYS(0, 0, close(3)); + EXPECT_TRUE(MemoryExists(p+FRAMESIZE*0)); + EXPECT_TRUE(MemoryExists(p+FRAMESIZE*1)); + EXPECT_FALSE(MemoryExists(p+FRAMESIZE*2)); + EXPECT_TRUE(MemoryExists(p+FRAMESIZE*3)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE*5)); + EXPECT_FALSE(MemoryExists(p+FRAMESIZE*0)); + EXPECT_FALSE(MemoryExists(p+FRAMESIZE*1)); + EXPECT_FALSE(MemoryExists(p+FRAMESIZE*2)); + EXPECT_FALSE(MemoryExists(p+FRAMESIZE*3)); +} +// clang-format on diff --git a/third_party/mbedtls/ssl_ciphersuites.c b/third_party/mbedtls/ssl_ciphersuites.c index 32d78b7c4..82e71b1e8 100644 --- a/third_party/mbedtls/ssl_ciphersuites.c +++ b/third_party/mbedtls/ssl_ciphersuites.c @@ -15,6 +15,7 @@ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/x86feature.h" #include "third_party/mbedtls/cipher.h" #include "third_party/mbedtls/common.h" #include "third_party/mbedtls/platform.h" @@ -44,10 +45,6 @@ asm(".include \"libc/disclaimer.inc\""); #if defined(MBEDTLS_SSL_TLS_C) const uint16_t ciphersuite_preference[] = { -#if defined(MBEDTLS_SSL_CIPHERSUITES) - MBEDTLS_SSL_CIPHERSUITES, -#else - #ifdef MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, @@ -175,7 +172,141 @@ const uint16_t ciphersuite_preference[] = MBEDTLS_TLS_PSK_WITH_NULL_SHA, #endif -#endif /* MBEDTLS_SSL_CIPHERSUITES */ + 0 +}; + +// if we don't have aes-ni then chacha will do a +// better job guarding against timing attacks +const uint16_t ciphersuite_preference_nehalem[] = +{ + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, +#endif + +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, +#endif + +#ifdef MBEDTLS_DES_C + MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA, // e.g. IE 8 XP + MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA, +#endif + +#ifdef MBEDTLS_ENABLE_WEAK_CIPHERSUITES + MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA, +#endif + +#ifdef MBEDTLS_CIPHER_NULL_CIPHER + MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_WITH_NULL_MD5, + MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_PSK_WITH_NULL_SHA, +#endif + 0 }; @@ -1338,12 +1469,6 @@ static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = 0, 0, 0, 0, 0 } }; -#if defined(MBEDTLS_SSL_CIPHERSUITES) -const uint16_t *mbedtls_ssl_list_ciphersuites( void ) -{ - return( ciphersuite_preference ); -} -#else #define MAX_CIPHERSUITES sizeof( ciphersuite_definitions ) / \ sizeof( ciphersuite_definitions[0] ) static uint16_t supported_ciphersuites[MAX_CIPHERSUITES]; @@ -1375,7 +1500,12 @@ const uint16_t *mbedtls_ssl_list_ciphersuites( void ) const uint16_t *p; uint16_t *q; - for( p = ciphersuite_preference, q = supported_ciphersuites; + if( X86_HAVE( AES ) ) + p = ciphersuite_preference; + else + p = ciphersuite_preference_nehalem; + + for( q = supported_ciphersuites; *p != 0 && q < supported_ciphersuites + MAX_CIPHERSUITES - 1; p++ ) { @@ -1393,7 +1523,6 @@ const uint16_t *mbedtls_ssl_list_ciphersuites( void ) return( supported_ciphersuites ); } -#endif /* MBEDTLS_SSL_CIPHERSUITES */ const mbedtls_ssl_ciphersuite_t *mbedtls_ssl_ciphersuite_from_string( const char *ciphersuite_name ) diff --git a/third_party/mbedtls/ssl_tls.c b/third_party/mbedtls/ssl_tls.c index 78e8edd0e..0fa09b0f6 100644 --- a/third_party/mbedtls/ssl_tls.c +++ b/third_party/mbedtls/ssl_tls.c @@ -7605,6 +7605,7 @@ static uint16_t ssl_preset_suiteb_ciphersuites[] = { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, 0 }; diff --git a/third_party/python/Objects/obmalloc.c b/third_party/python/Objects/obmalloc.c index fb5515732..93ec3d515 100644 --- a/third_party/python/Objects/obmalloc.c +++ b/third_party/python/Objects/obmalloc.c @@ -1984,8 +1984,8 @@ _PyMem_DebugRawAlloc(int use_calloc, void *ctx, size_t nbytes) _PyMem_DebugCheckAddress(api->api_id, p+2*SST); if (IsAsan()) { - __asan_poison((uintptr_t)(p + SST + 1), SST-1, kAsanHeapUnderrun); - __asan_poison((uintptr_t)tail, SST, kAsanHeapOverrun); + __asan_poison((p + SST + 1), SST-1, kAsanHeapUnderrun); + __asan_poison(tail, SST, kAsanHeapOverrun); } return p + 2*SST; @@ -2041,7 +2041,7 @@ _PyMem_DebugRawFree(void *ctx, void *p) nbytes += 4*SST; if (nbytes > 0) { if (IsAsan()) { - __asan_unpoison((uintptr_t)q, nbytes); + __asan_unpoison(q, nbytes); } memset(q, DEADBYTE, nbytes); } @@ -2080,12 +2080,12 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes) tail = q + nbytes; w = 0x0101010101010101ull * FORBIDDENBYTE; WRITE64LE(tail, w); - if (IsAsan()) __asan_poison((uintptr_t)tail, SST, kAsanHeapOverrun); + if (IsAsan()) __asan_poison(tail, SST, kAsanHeapOverrun); write_size_t(tail + SST, serialno); if (nbytes > original_nbytes) { /* growing: mark new extra memory clean */ if (IsAsan()) { - __asan_unpoison((uintptr_t)(q + original_nbytes), + __asan_unpoison((q + original_nbytes), nbytes - original_nbytes); } memset(q + original_nbytes, CLEANBYTE, diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 1d1e6bbda..f5983ef9b 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -432,7 +432,7 @@ void HandleClient(void) { WARNF("%s got unexpected input event from client %#x", exename, fds[0].revents); } - WARNF("%s client disconnected so killing worker", exename); + WARNF("%s client disconnected so killing worker %d", exename, child); LOGIFNEG1(kill(child, 9)); LOGIFNEG1(waitpid(child, 0, 0)); LOGIFNEG1(close(g_clifd)); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index d5e3ce614..8bf8de2ab 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -7064,6 +7064,10 @@ static void TlsInit(void) { int suite; if (unsecure) return; + if (suiteb && !X86_HAVE(AES)) { + WARNF("you're using suite b crypto but don't have aes-ni"); + } + if (!sslinitialized) { InitializeRng(&rng); InitializeRng(&rngcli); diff --git a/tool/viz/cpuid.c b/tool/viz/cpuid.c index d10c36394..3ba6ab3ba 100644 --- a/tool/viz/cpuid.c +++ b/tool/viz/cpuid.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/intrin/kprintf.h" #include "libc/log/color.internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/cpuid4.internal.h" @@ -92,6 +93,7 @@ void showcachesizes(void) { int main(int argc, char *argv[]) { int x; long tsc_aux; + ShowCrashReports(); showvendor(); showmodel(); @@ -106,14 +108,19 @@ int main(int argc, char *argv[]) { } if (X86_HAVE(HYPERVISOR)) { - unsigned eax, ebx, ecx, edx; - asm("push\t%%rbx\n\t" - "cpuid\n\t" - "mov\t%%ebx,%1\n\t" + int ax, cx; + char s[4 * 3 + 1]; + asm("push\t%%rbx\r\n" + "cpuid\r\n" + "mov\t%%ebx,0+%2\r\n" + "mov\t%%ecx,4+%2\r\n" + "mov\t%%edx,8+%2\r\n" + "movb\t$0,12+%2\r\n" "pop\t%%rbx" - : "=a"(eax), "=rm"(ebx), "=c"(ecx), "=d"(edx) - : "0"(0x40000000), "2"(0)); - printf("Running inside %.4s%.4s%.4s (eax=%#x)\n", &ebx, &ecx, &edx, eax); + : "=a"(ax), "=c"(cx), "=o"(s) + : "0"(0x40000000), "1"(0) + : "rdx"); + kprintf("Running inside %s (eax=%#x)\n", s, ax); } printf("\n"); From ec2cb880580e3fc47f08b7bcd26e645094b7f6cf Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 19 May 2022 16:57:49 -0700 Subject: [PATCH 18/40] Make fixes and improvements - Document more compiler flags - Expose new __print_maps() api - Better overflow checking in mmap() - Improve the shell example somewhat - Fix minor runtime bugs regarding stacks - Make kill() on fork()+execve()'d children work - Support CLONE_CHILD_CLEARTID for proper joining - Fix recent possible deadlock regression with --ftrace --- ape/ape.lds | 8 +- examples/compress.c | 41 ++- examples/decompress.c | 25 +- examples/greenbean.c | 5 +- examples/shell.c | 145 ++++++++-- libc/calls/kill-nt.c | 30 +- libc/calls/sigaltstack.c | 10 +- libc/calls/wincrash.c | 1 + libc/fmt/fmt.mk | 10 + libc/fmt/{strerror_r.greg.c => strerror_r.c} | 0 libc/integral/c.inc | 4 +- libc/intrin/_spinlock_debug_1.c | 56 ++++ libc/intrin/_spinlock_debug_4.c | 57 ++++ libc/intrin/asan.c | 22 +- libc/intrin/getmagnumstr.greg.c | 2 +- libc/intrin/gettid.greg.c | 7 + libc/intrin/intrin.mk | 12 +- libc/{fmt => intrin}/kerrnodocs.S | 0 libc/{fmt => intrin}/kerrnonames.S | 0 libc/intrin/kprintf.greg.c | 14 +- libc/intrin/lockcmpxchgp.h | 24 ++ libc/intrin/spinlock.h | 100 +++---- libc/{fmt => intrin}/strerdoc.greg.c | 2 +- libc/{fmt => intrin}/strerrno.greg.c | 2 +- libc/{fmt => intrin}/strerror_wr.greg.c | 2 +- libc/intrin/tls.greg.c | 14 + libc/log/checkfail.c | 2 +- libc/log/internal.h | 2 +- libc/log/leaks.c | 2 +- libc/log/oncrash.c | 14 +- libc/log/showcrashreports.c | 65 +++-- libc/nexgen32e/checkstackalign.S | 4 +- libc/nexgen32e/nexgen32e.h | 2 +- libc/nexgen32e/nt2sysv.S | 2 + libc/nt/enum/th32cs.h | 11 + libc/nt/kernel32/CreateToolhelp32Snapshot.s | 12 +- libc/nt/kernel32/Process32FirstW.s | 12 +- libc/nt/kernel32/Process32NextW.s | 12 +- libc/nt/master.sh | 6 +- libc/nt/process.h | 5 + libc/nt/struct/processentry32.h | 19 ++ libc/runtime/{clone.greg.c => clone.c} | 262 +++++++++++------- libc/runtime/ftracer.c | 55 +++- ...getsymboltable.greg.c => getsymboltable.c} | 32 ++- libc/runtime/memtrack.greg.c | 10 +- libc/runtime/memtrack.internal.h | 3 +- libc/runtime/mmap.c | 171 +++++++----- libc/runtime/printargs.greg.c | 2 +- libc/runtime/printmaps.c | 30 ++ libc/runtime/runtime.h | 1 + libc/runtime/runtime.mk | 4 +- libc/runtime/stack.h | 33 ++- libc/runtime/winthreadlaunch.S | 46 +++ libc/stdio/unlocked/stdio_unlock.S | 1 + libc/str/str.mk | 8 + libc/str/strsignal.greg.c | 4 +- libc/testlib/quota.c | 4 +- test/libc/calls/sigaction_test.c | 8 +- test/libc/log/backtrace_test.c | 1 + test/libc/rand/rand64_test.c | 32 +-- test/libc/runtime/clone_test.c | 55 ++-- test/libc/runtime/mmap_test.c | 57 +++- test/libc/runtime/munmap_test.c | 8 +- third_party/dlmalloc/dlmalloc.mk | 2 +- .../dlmalloc/{vespene.greg.c => vespene.c} | 0 third_party/lua/lrepl.c | 2 +- tool/build/runit.c | 4 +- tool/net/redbean.c | 44 +-- 68 files changed, 1211 insertions(+), 431 deletions(-) rename libc/fmt/{strerror_r.greg.c => strerror_r.c} (100%) create mode 100644 libc/intrin/_spinlock_debug_1.c create mode 100644 libc/intrin/_spinlock_debug_4.c rename libc/{fmt => intrin}/kerrnodocs.S (100%) rename libc/{fmt => intrin}/kerrnonames.S (100%) create mode 100644 libc/intrin/lockcmpxchgp.h rename libc/{fmt => intrin}/strerdoc.greg.c (98%) rename libc/{fmt => intrin}/strerrno.greg.c (98%) rename libc/{fmt => intrin}/strerror_wr.greg.c (97%) create mode 100644 libc/nt/enum/th32cs.h create mode 100644 libc/nt/struct/processentry32.h rename libc/runtime/{clone.greg.c => clone.c} (68%) rename libc/runtime/{getsymboltable.greg.c => getsymboltable.c} (88%) create mode 100644 libc/runtime/printmaps.c create mode 100644 libc/runtime/winthreadlaunch.S rename third_party/dlmalloc/{vespene.greg.c => vespene.c} (100%) diff --git a/ape/ape.lds b/ape/ape.lds index caac90617..4e00ad30d 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -510,7 +510,7 @@ HIDDEN(ape_ram_rva = RVA(ape_ram_vaddr)); HIDDEN(ape_stack_pf = DEFINED(ape_stack_pf) ? ape_stack_pf : PF_R | PF_W); HIDDEN(ape_stack_prot = _PF2PROT(ape_stack_pf)); HIDDEN(ape_stack_offset = ape_ram_offset + ape_ram_filesz); -HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000 - STACKSIZE); +HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000); HIDDEN(ape_stack_paddr = ape_ram_paddr + ape_ram_filesz); HIDDEN(ape_stack_filesz = 0); HIDDEN(ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : STACKSIZE); @@ -697,6 +697,12 @@ ASSERT(DEFINED(_start) || DEFINED(_start16), ASSERT(!DEFINED(_start16) || REAL(_end) < 65536, "ape won't support non-tiny real mode programs"); +ASSERT(IS2POW(ape_stack_memsz), + "ape_stack_memsz must be a two power"); + +ASSERT(!(ape_stack_vaddr & (ape_stack_memsz - 1)), + "ape_stack_vaddr must have ape_stack_memsz alignment; try using STATIC_STACK_ADDR(0x700000000000 - ape_stack_memsz);"); + /* Let's not be like Knight Capital. */ /* NOCROSSREFS_TO(.test .text) */ diff --git a/examples/compress.c b/examples/compress.c index 800e9532f..640672b66 100644 --- a/examples/compress.c +++ b/examples/compress.c @@ -9,6 +9,7 @@ #endif #include "libc/assert.h" #include "libc/errno.h" +#include "libc/fmt/conv.h" #include "libc/log/check.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" @@ -16,13 +17,36 @@ #include "libc/str/str.h" #include "third_party/zlib/zlib.h" -#define CHUNK 4096 +#define CHUNK 32768 // clang-format off // make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b +/* +# data file is o/dbg/third_party/python/python.com +# level 0 147517 compress 495 MB/s decompress 1.4 GB/s +# level 1 80274 compress 29.2 MB/s decompress 303 MB/s +# level 2 79384 compress 33.8 MB/s decompress 212 MB/s +# level 3 78875 compress 28.9 MB/s decompress 224 MB/s +# level 4 78010 compress 27.1 MB/s decompress 319 MB/s <-- sweet spot? +# level 5 77107 compress 19.5 MB/s decompress 273 MB/s +# level 6 75081 compress 10.0 MB/s decompress 99.3 MB/s +# level 7 75022 compress 7.5 MB/s decompress 287 MB/s +# level 8 75016 compress 5.4 MB/s decompress 109 MB/s +# level 9 75016 compress 5.4 MB/s decompress 344 MB/s +m= +make -j8 MODE=$m o/$m/examples || exit +for level in $(seq 0 9); do + o/$m/examples/compress.com $level /tmp/info >/tmp/comp + compspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info) + o/$m/examples/decompress.com $level /tmp/info >/dev/null + decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info) + size=$(o/$m/examples/compress.com $level 1) { + level = atoi(argv[1]); + } else { + level = Z_DEFAULT_COMPRESSION; + } + rc = compressor(0, 1, level); if (rc == Z_OK) { return 0; } else { diff --git a/examples/decompress.c b/examples/decompress.c index a8b483b6d..657a0a4ed 100644 --- a/examples/decompress.c +++ b/examples/decompress.c @@ -14,10 +14,33 @@ #include "libc/stdio/stdio.h" #include "third_party/zlib/zlib.h" -#define CHUNK 4096 +#define CHUNK 32768 // clang-format off // make -j8 o//examples && dd if=/dev/urandom count=100 | tee a | o//examples/compress.com | o//examples/decompress.com >b && sha1sum a b +/* +# data file is o/dbg/third_party/python/python.com +# level 0 147517 compress 495 MB/s decompress 1.4 GB/s +# level 1 80274 compress 29.2 MB/s decompress 303 MB/s +# level 2 79384 compress 33.8 MB/s decompress 212 MB/s +# level 3 78875 compress 28.9 MB/s decompress 224 MB/s +# level 4 78010 compress 27.1 MB/s decompress 319 MB/s <-- sweet spot? +# level 5 77107 compress 19.5 MB/s decompress 273 MB/s +# level 6 75081 compress 10.0 MB/s decompress 99.3 MB/s +# level 7 75022 compress 7.5 MB/s decompress 287 MB/s +# level 8 75016 compress 5.4 MB/s decompress 109 MB/s +# level 9 75016 compress 5.4 MB/s decompress 344 MB/s +m= +make -j8 MODE=$m o/$m/examples || exit +for level in $(seq 0 9); do + o/$m/examples/compress.com $level /tmp/info >/tmp/comp + compspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info) + o/$m/examples/decompress.com $level /tmp/info >/dev/null + decompspeed=$(grep -Po '[.\d]+ \w+/s' /tmp/info) + size=$(o/$m/examples/compress.com $level : "); +} + int main(int argc, char *argv[]) { bool timeit; int64_t nanos; - int n, ws, pid; struct rusage ru; struct timespec ts1, ts2; char *prog, path[PATH_MAX]; sigset_t chldmask, savemask; - struct sigaction ignore, saveint, savequit; - char *p, *line, **args, *arg, *start, *state, prompt[64]; + int stdoutflags, stderrflags; + const char *stdoutpath, *stderrpath; + int n, rc, ws, pid, child, killcount; + struct sigaction sa, saveint, savequit; + char *p, *line, **args, *arg, *start, *state, prompt[1024]; linenoiseSetFreeHintsCallback(free); linenoiseSetHintsCallback(ShellHint); linenoiseSetCompletionCallback(ShellCompletion); - stpcpy(prompt, "$ "); + MakePrompt(prompt); while ((line = linenoiseWithHistory(prompt, "cmd"))) { n = 0; start = line; @@ -129,35 +157,114 @@ int main(int argc, char *argv[]) { } else { timeit = false; } + stdoutpath = 0; + stderrpath = 0; + stdoutflags = 0; + stderrflags = 0; args = xcalloc(1, sizeof(*args)); while ((arg = strtok_r(start, " \t\r\n", &state))) { - args = xrealloc(args, (++n + 1) * sizeof(*args)); - args[n - 1] = arg; - args[n - 0] = 0; - start = 0; + // cmd >>stdout.txt + if (arg[0] == '>' && arg[1] == '>') { + stdoutflags = O_WRONLY | O_APPEND | O_CREAT; + stdoutpath = arg + 2; + } else if (arg[0] == '>') { + // cmd >stdout.txt + stdoutflags = O_WRONLY | O_CREAT | O_TRUNC; + stdoutpath = arg + 1; + } else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') { + // cmd 2>>stderr.txt + stderrflags = O_WRONLY | O_APPEND | O_CREAT; + stderrpath = arg + 3; + } else if (arg[0] == '2' && arg[1] == '>') { + // cmd 2>stderr.txt + stderrflags = O_WRONLY | O_CREAT | O_TRUNC; + stderrpath = arg + 2; + } else { + // arg + args = xrealloc(args, (++n + 1) * sizeof(*args)); + args[n - 1] = arg; + args[n - 0] = 0; + start = 0; + } } if (n > 0) { if ((prog = commandv(args[0], path, sizeof(path)))) { - ignore.sa_flags = 0; - ignore.sa_handler = SIG_IGN; - sigemptyset(&ignore.sa_mask); - sigaction(SIGINT, &ignore, &saveint); - sigaction(SIGQUIT, &ignore, &savequit); + + // let keyboard interrupts kill child and not shell + gotint = 0; + killcount = 0; + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sigaction(SIGQUIT, &sa, &savequit); + sa.sa_handler = OnInterrupt; + sigaction(SIGINT, &sa, &saveint); sigemptyset(&chldmask); sigaddset(&chldmask, SIGCHLD); sigprocmask(SIG_BLOCK, &chldmask, &savemask); + // record timestamp if (timeit) { clock_gettime(CLOCK_REALTIME, &ts1); } - if (!fork()) { + + // launch process + if (!(child = vfork())) { + if (stdoutpath) { + close(1); + open(stdoutpath, stdoutflags, 0644); + } + if (stderrpath) { + close(2); + open(stderrpath, stderrflags, 0644); + } sigaction(SIGINT, &saveint, 0); sigaction(SIGQUIT, &savequit, 0); sigprocmask(SIG_SETMASK, &savemask, 0); execv(prog, args); _Exit(127); } - wait4(0, &ws, 0, &ru); + + // wait for process + for (;;) { + if (gotint) { + switch (killcount) { + case 0: + // ctrl-c + // we do nothing + // terminals broadcast sigint to process group + rc = 0; + break; + case 1: + // ctrl-c ctrl-c + // we try sending sigterm + rc = kill(child, SIGTERM); + break; + default: + // ctrl-c ctrl-c ctrl-c ... + // we use kill -9 as our last resort + rc = kill(child, SIGKILL); + break; + } + if (rc == -1) { + fprintf(stderr, "kill failed: %m\n"); + exit(1); + } + ++killcount; + gotint = 0; + } + rc = wait4(0, &ws, 0, &ru); + if (rc != -1) { + break; + } else if (errno == EINTR) { + errno = 0; + } else { + fprintf(stderr, "wait failed: %m\n"); + exit(1); + } + } + + // print resource consumption for `time` pseudocommand if (timeit) { clock_gettime(CLOCK_REALTIME, &ts2); if (ts2.tv_sec == ts1.tv_sec) { @@ -174,23 +281,27 @@ int main(int argc, char *argv[]) { free(p); } + // update prompt to reflect exit status p = prompt; if (WIFEXITED(ws)) { if (WEXITSTATUS(ws)) { if (!__nocolor) p = stpcpy(p, "\e[1;31m"); p = stpcpy(p, "rc="); p = FormatInt32(p, WEXITSTATUS(ws)); + if (128 < WEXITSTATUS(ws) && WEXITSTATUS(ws) <= 128 + 32) { + *p++ = ' '; + p = stpcpy(p, strsignal(WEXITSTATUS(ws) - 128)); + } if (!__nocolor) p = stpcpy(p, "\e[0m"); *p++ = ' '; } } else { if (!__nocolor) p = stpcpy(p, "\e[1;31m"); - p = stpcpy(p, "rc="); p = stpcpy(p, strsignal(WTERMSIG(ws))); if (!__nocolor) p = stpcpy(p, "\e[0m"); *p++ = ' '; } - p = stpcpy(p, "$ "); + MakePrompt(p); sigaction(SIGINT, &saveint, 0); sigaction(SIGQUIT, &savequit, 0); diff --git a/libc/calls/kill-nt.c b/libc/calls/kill-nt.c index 9dc6d46a0..60f909fc3 100644 --- a/libc/calls/kill-nt.c +++ b/libc/calls/kill-nt.c @@ -20,18 +20,21 @@ #include "libc/calls/getconsolectrlevent.internal.h" #include "libc/calls/internal.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.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/errfuns.h" textwindows int sys_kill_nt(int pid, int sig) { bool32 ok; - int64_t handle; + int64_t h; int event, ntpid; // is killing everything except init really worth supporting? @@ -68,20 +71,37 @@ textwindows int sys_kill_nt(int pid, int sig) { } } - // XXX: Is this a cosmo pid that was returned by fork_nt? + // is this a cosmo pid that was returned by fork? if (__isfdkind(pid, kFdProcess)) { + // 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 + int64_t hSnap, hProc, hChildProc; + struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)}; + ntpid = GetProcessId(g_fds.p[pid].handle); + hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0); + if (Process32First(hSnap, &pe)) { + do { + if (pe.th32ParentProcessID == ntpid) { + if ((h = OpenProcess(kNtProcessTerminate, false, pe.th32ProcessID))) { + TerminateProcess(h, 128 + sig); + CloseHandle(h); + } + } + } while (Process32Next(hSnap, &pe)); + } ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig); if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true; return 0; } // XXX: Is this a raw new technology pid? Because that's messy. - if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) { - ok = TerminateProcess(handle, 128 + sig); + if ((h = OpenProcess(kNtProcessTerminate, false, pid))) { + ok = TerminateProcess(h, 128 + sig); if (!ok && GetLastError() == kNtErrorAccessDenied) { ok = true; // cargo culting other codebases here } - CloseHandle(handle); + CloseHandle(h); return 0; } else { return -1; diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c index 4ec6f30f5..4839b9dc3 100644 --- a/libc/calls/sigaltstack.c +++ b/libc/calls/sigaltstack.c @@ -57,8 +57,9 @@ static void sigaltstack2linux(struct sigaltstack *linux, * struct sigaction sa; * struct sigaltstack ss; * ss.ss_flags = 0; - * ss.ss_size = SIGSTKSZ; - * ss.ss_sp = malloc(ss.ss_size); + * ss.ss_size = GetStackSize(); + * ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + * MAP_STACK | MAP_ANONYMOUS, -1, 0); * sa.sa_flags = SA_ONSTACK; * sa.sa_handler = OnStackOverflow; * __cxa_atexit(free, ss[0].ss_sp, 0); @@ -66,6 +67,11 @@ static void sigaltstack2linux(struct sigaltstack *linux, * sigaltstack(&ss, 0); * sigaction(SIGSEGV, &sa, 0); * + * It's strongly recommended that you allocate a stack with the same + * size as GetStackSize() and that it have GetStackSize() alignment. + * Otherwise some of your runtime support code (e.g. ftrace stack use + * logging, kprintf() memory safety) won't be able to work as well. + * * @param neu if non-null will install new signal alt stack * @param old if non-null will receive current signal alt stack * @return 0 on success, or -1 w/ errno diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index fbe717a5e..7c850d6ec 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -35,6 +35,7 @@ privileged unsigned __wincrash(struct NtExceptionPointers *ep) { int sig, code; ucontext_t ctx; STRACE("__wincrash"); + switch (ep->ExceptionRecord->ExceptionCode) { case kNtSignalBreakpoint: code = TRAP_BRKPT; diff --git a/libc/fmt/fmt.mk b/libc/fmt/fmt.mk index ffd33e912..6ff36f863 100644 --- a/libc/fmt/fmt.mk +++ b/libc/fmt/fmt.mk @@ -77,6 +77,16 @@ o/$(MODE)/libc/fmt/wcstoumax.o: \ OVERRIDE_CFLAGS += \ -Os +# we can't use compiler magic because: +# kprintf() depends on these functions +o/$(MODE)/libc/fmt/strerrno.greg.o \ +o/$(MODE)/libc/fmt/strerrdoc.greg.o \ +o/$(MODE)/libc/fmt/strerror_wr.greg.o: \ + OVERRIDE_CFLAGS += \ + -fpie \ + -ffreestanding \ + $(NO_MAGIC) + LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x))) LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS)) LIBC_FMT_HDRS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/fmt/strerror_r.greg.c b/libc/fmt/strerror_r.c similarity index 100% rename from libc/fmt/strerror_r.greg.c rename to libc/fmt/strerror_r.c diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 57ea8b3d6..05234e6d8 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -195,9 +195,9 @@ typedef struct { #ifndef privileged #if !defined(__STRICT_ANSI__) && \ (__has_attribute(__visibility__) || defined(__GNUC__)) -#define privileged _Section(".privileged") noinstrument +#define privileged _Section(".privileged") #else -#define privileged _Section(".privileged") noinstrument +#define privileged _Section(".privileged") #endif #endif diff --git a/libc/intrin/_spinlock_debug_1.c b/libc/intrin/_spinlock_debug_1.c new file mode 100644 index 000000000..126e47099 --- /dev/null +++ b/libc/intrin/_spinlock_debug_1.c @@ -0,0 +1,56 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchgp.h" +#include "libc/intrin/spinlock.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/time/clockstonanos.internal.h" + +void _spinlock_debug_1(void *lockptr, const char *lockname, const char *file, + int line, const char *func) { + unsigned i; + uint64_t ts1, ts2; + int me, owner, *lock = lockptr; + me = gettid(); + owner = 0; + if (!_lockcmpxchgp(lock, &owner, me)) { + if (owner == me) { + kprintf("%s:%d: warning: possible re-entry on lock %s in %s()\n", file, + line, lockname, func); + } + i = 0; + ts1 = rdtsc(); + for (;;) { + owner = 0; + if (_lockcmpxchgp(lock, &owner, me)) break; + ts2 = rdtsc(); + if (ClocksToNanos(ts1, ts2) > 1000000000ul) { + ts1 = ts2; + kprintf("%s:%d: warning: slow lock on %s in %s()\n", file, line, + lockname, func); + } + if (++i & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } + } + } +} diff --git a/libc/intrin/_spinlock_debug_4.c b/libc/intrin/_spinlock_debug_4.c new file mode 100644 index 000000000..e42dfbbcf --- /dev/null +++ b/libc/intrin/_spinlock_debug_4.c @@ -0,0 +1,57 @@ +/*-*- 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/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchgp.h" +#include "libc/intrin/spinlock.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/time/clockstonanos.internal.h" + +void _spinlock_debug_4(void *lockptr, const char *lockname, const char *file, + int line, const char *func) { + unsigned i; + uint64_t ts1, ts2; + int me, owner, *lock = lockptr; + me = gettid(); + owner = 0; + if (!_lockcmpxchgp(lock, &owner, me)) { + if (owner == me) { + kprintf("%s:%d: warning: lock re-entry on %s in %s()\n", file, line, + lockname, func); + _Exit(1); + } + i = 0; + ts1 = rdtsc(); + for (;;) { + owner = 0; + if (_lockcmpxchgp(lock, &owner, me)) break; + ts2 = rdtsc(); + if (ClocksToNanos(ts1, ts2) > 1000000000ul) { + ts1 = ts2; + kprintf("%s:%d: warning: slow lock on %s in %s()\n", file, line, + lockname, func); + } + if (++i & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } + } + } +} diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 7b17daa53..d8bf1bd89 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -59,9 +59,21 @@ STATIC_YOINK("_init_asan"); +#if IsModeDbg() +// MODE=dbg +// O(32mb) of morgue memory +// Θ(64) bytes of malloc overhead #define ASAN_MORGUE_ITEMS 512 -#define ASAN_MORGUE_THRESHOLD 65536 // morgue memory O(ITEMS*THRESHOLD) -#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin +#define ASAN_MORGUE_THRESHOLD 65536 +#define ASAN_TRACE_ITEMS 16 +#else +// MODE=asan +// O(32mb) of morgue memory +// Θ(32) bytes of malloc overhead +#define ASAN_MORGUE_ITEMS 512 +#define ASAN_MORGUE_THRESHOLD 65536 +#define ASAN_TRACE_ITEMS 4 +#endif /** * @fileoverview Cosmopolitan Address Sanitizer Runtime. @@ -853,7 +865,7 @@ static void __asan_morgue_flush(void) { void *p; _spinlock_cooperative(&__asan_lock); for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { - if (weaken(dlfree)) { + if (__asan_morgue.p[i] && weaken(dlfree)) { weaken(dlfree)(__asan_morgue.p[i]); } __asan_morgue.p[i] = 0; @@ -1196,9 +1208,9 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) { struct AsanTrace tr; __asan_rawtrace(&tr, __builtin_frame_address(0)); kprintf( - "WARNING: ASAN %s %s bad %d byte %s at %x bt %x %x %x %x %x\n", + "WARNING: ASAN %s %s bad %d byte %s at %x bt %x %x %x\n", __asan_noreentry == gettid() ? "error during" : "multi-threaded crash", - s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3], tr.p[4], tr.p[5]); + s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3]); } void __asan_report_load(uint8_t *addr, int size) { diff --git a/libc/intrin/getmagnumstr.greg.c b/libc/intrin/getmagnumstr.greg.c index 20834e936..7bcbbc542 100644 --- a/libc/intrin/getmagnumstr.greg.c +++ b/libc/intrin/getmagnumstr.greg.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/magnumstrs.internal.h" -char *GetMagnumStr(const struct MagnumStr *ms, int x) { +privileged char *GetMagnumStr(const struct MagnumStr *ms, int x) { int i; for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { if (x == MAGNUM_NUMBER(ms, i)) { diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index 4331cfbd8..95a77a7ad 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -30,6 +30,13 @@ privileged int gettid(void) { int64_t wut; struct WinThread *wt; + if (__tls_enabled) { + rc = *(int *)(__get_tls() + 0x38); + if (rc && rc != -1) { + return rc; + } + } + if (IsWindows()) { return GetCurrentThreadId(); } diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index f3f671ea5..7e10b36e9 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -58,11 +58,21 @@ o/$(MODE)/libc/intrin/asan.o: \ -finline \ -finline-functions +# we can't use compiler magic because: +# kprintf() is mission critical to error reporting +o/$(MODE)/libc/intrin/getmagnumstr.greg.o \ +o/$(MODE)/libc/intrin/strerrno.greg.o \ +o/$(MODE)/libc/intrin/strerrdoc.greg.o \ +o/$(MODE)/libc/intrin/strerror_wr.greg.o \ o/$(MODE)/libc/intrin/kprintf.greg.o: \ OVERRIDE_CFLAGS += \ -fpie \ + -fwrapv \ + -x-no-pg \ + -mno-fentry \ -ffreestanding \ - $(NO_MAGIC) + -fno-sanitize=all \ + -fno-stack-protector o/$(MODE)/libc/intrin/tls.greg.o \ o/$(MODE)/libc/intrin/exit.greg.o \ diff --git a/libc/fmt/kerrnodocs.S b/libc/intrin/kerrnodocs.S similarity index 100% rename from libc/fmt/kerrnodocs.S rename to libc/intrin/kerrnodocs.S diff --git a/libc/fmt/kerrnonames.S b/libc/intrin/kerrnonames.S similarity index 100% rename from libc/fmt/kerrnonames.S rename to libc/intrin/kerrnonames.S diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index c07ecd2e8..034075d47 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -52,6 +52,8 @@ #include "libc/sysv/consts/prot.h" #include "libc/time/clockstonanos.internal.h" +extern hidden struct SymbolTable *__symtab; + struct Timestamps { unsigned long long birth; unsigned long long start; @@ -515,13 +517,19 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, } case 't': { + // %t will print the &symbol associated with an address. this + // requires that some other code linked GetSymbolTable() and + // called it beforehand to ensure the symbol table is loaded. + // if the symbol table isn't linked or available, then this + // routine will display &hexaddr so objdump -dS foo.com.dbg + // can be manually consulted to look up the faulting code. int idx; x = va_arg(va, intptr_t); - if (weaken(__get_symbol) && + if (weaken(__symtab) && *weaken(__symtab) && (idx = weaken(__get_symbol)(0, x)) != -1) { if (p + 1 <= e) *p++ = '&'; - s = weaken(GetSymbolTable)()->name_base + - weaken(GetSymbolTable)()->names[idx]; + s = (*weaken(__symtab))->name_base + + (*weaken(__symtab))->names[idx]; goto FormatString; } base = 4; diff --git a/libc/intrin/lockcmpxchgp.h b/libc/intrin/lockcmpxchgp.h new file mode 100644 index 000000000..fdf12d3ab --- /dev/null +++ b/libc/intrin/lockcmpxchgp.h @@ -0,0 +1,24 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHGP_H_ +#define COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHGP_H_ +#include "libc/bits/asmflag.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) +#define _lockcmpxchgp(IN_OUT_IFTHING, IN_OUT_ISEQUALTOME, IN_REPLACEITWITHME) \ + ({ \ + bool DidIt; \ + autotype(IN_OUT_IFTHING) IfThing = (IN_OUT_IFTHING); \ + typeof(IfThing) IsEqualToMe = (IN_OUT_ISEQUALTOME); \ + typeof(*IfThing) ReplaceItWithMe = (IN_REPLACEITWITHME); \ + asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \ + : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(*IsEqualToMe) \ + : "r"(ReplaceItWithMe) \ + : "cc"); \ + DidIt; \ + }) +#endif /* GNUC && !ANSI && x86 */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHGP_H_ */ diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index c58c72f23..0b6d9eb8c 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -1,8 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ #define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ +#include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchg.h" +#include "libc/intrin/lockcmpxchgp.h" #if IsModeDbg() && !defined(_SPINLOCK_DEBUG) #define _SPINLOCK_DEBUG @@ -19,63 +20,62 @@ #define _spinlock_ndebug(lock) _spinlock_cooperative(lock) #endif -#define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED) - #define _trylock(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST) -#define _seizelock(lock) \ - do { \ - typeof(*(lock)) x = 1; \ - __atomic_store(lock, &x, __ATOMIC_RELEASE); \ +#define _spunlock(lock) \ + do { \ + autotype(lock) __lock = (lock); \ + typeof(*__lock) __x = 0; \ + __atomic_store(__lock, &__x, __ATOMIC_RELAXED); \ } while (0) -#define _spinlock_tiny(lock) \ - do { \ - while (_trylock(lock)) { \ - __builtin_ia32_pause(); \ - } \ +#define _seizelock(lock) \ + do { \ + autotype(lock) __lock = (lock); \ + typeof(*__lock) __x = 1; \ + __atomic_store(__lock, &__x, __ATOMIC_RELEASE); \ } while (0) -#define _spinlock_cooperative(lock) \ - do { \ - int __tries = 0; \ - for (;;) { \ - typeof(*(lock)) x; \ - __atomic_load(lock, &x, __ATOMIC_RELAXED); \ - if (!x && !_trylock(lock)) { \ - break; \ - } else if (++__tries & 7) { \ - __builtin_ia32_pause(); \ - } else { \ - sched_yield(); \ - } \ - } \ +#define _spinlock_tiny(lock) \ + do { \ + autotype(lock) __lock = (lock); \ + while (_trylock(__lock)) { \ + __builtin_ia32_pause(); \ + } \ } while (0) -#define _spinlock_debug(lock) \ - do { \ - typeof(*(lock)) me, owner; \ - unsigned long warntries = 16777216; \ - me = gettid(); \ - if (!_lockcmpxchg(lock, 0, me)) { \ - __atomic_load(lock, &owner, __ATOMIC_RELAXED); \ - if (owner == me) { \ - kprintf("%s:%d: warning: possible re-entry on %s in %s()\n", __FILE__, \ - __LINE__, #lock, __FUNCTION__); \ - } \ - while (!_lockcmpxchg(lock, 0, me)) { \ - if (!--warntries) { \ - warntries = -1; \ - kprintf("%s:%d: warning: possible deadlock on %s in %s()\n", \ - __FILE__, __LINE__, #lock, __FUNCTION__); \ - } \ - if (warntries & 7) { \ - __builtin_ia32_pause(); \ - } else { \ - sched_yield(); \ - } \ - } \ - } \ +#define _spinlock_cooperative(lock) \ + do { \ + autotype(lock) __lock = (lock); \ + typeof(*__lock) __x; \ + int __tries = 0; \ + for (;;) { \ + __atomic_load(__lock, &__x, __ATOMIC_RELAXED); \ + if (!__x && !_trylock(__lock)) { \ + break; \ + } else if (++__tries & 7) { \ + __builtin_ia32_pause(); \ + } else { \ + sched_yield(); \ + } \ + } \ + } while (0) + +void _spinlock_debug_1(void *, const char *, const char *, int, const char *); +void _spinlock_debug_4(void *, const char *, const char *, int, const char *); + +#define _spinlock_debug(lock) \ + do { \ + switch (sizeof(*(lock))) { \ + case 1: \ + _spinlock_debug_1(lock, #lock, __FILE__, __LINE__, __FUNCTION__); \ + break; \ + case 4: \ + _spinlock_debug_4(lock, #lock, __FILE__, __LINE__, __FUNCTION__); \ + break; \ + default: \ + assert(!"unsupported size"); \ + } \ } while (0) #endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */ diff --git a/libc/fmt/strerdoc.greg.c b/libc/intrin/strerdoc.greg.c similarity index 98% rename from libc/fmt/strerdoc.greg.c rename to libc/intrin/strerdoc.greg.c index b8e89b384..07022eca7 100644 --- a/libc/fmt/strerdoc.greg.c +++ b/libc/intrin/strerdoc.greg.c @@ -23,7 +23,7 @@ * Converts errno value to descriptive sentence. * @return non-null rodata string or null if not found */ -char *strerdoc(int x) { +privileged char *strerdoc(int x) { if (x) { return GetMagnumStr(kErrnoDocs, x); } else { diff --git a/libc/fmt/strerrno.greg.c b/libc/intrin/strerrno.greg.c similarity index 98% rename from libc/fmt/strerrno.greg.c rename to libc/intrin/strerrno.greg.c index 145727831..3c9e1a037 100644 --- a/libc/fmt/strerrno.greg.c +++ b/libc/intrin/strerrno.greg.c @@ -23,7 +23,7 @@ * Converts errno value to symbolic name. * @return non-null rodata string or null if not found */ -char *strerrno(int x) { +privileged char *strerrno(int x) { if (x) { return GetMagnumStr(kErrnoNames, x); } else { diff --git a/libc/fmt/strerror_wr.greg.c b/libc/intrin/strerror_wr.greg.c similarity index 97% rename from libc/fmt/strerror_wr.greg.c rename to libc/intrin/strerror_wr.greg.c index 8fad73863..7832eb360 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/intrin/strerror_wr.greg.c @@ -31,7 +31,7 @@ * @param err is error number or zero if unknown * @return 0 on success, or error code */ -int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { +privileged int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { /* kprintf() weakly depends on this function */ int c, n; char16_t winmsg[256]; diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index 358172228..52366662c 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -32,10 +32,24 @@ /** * Initializes thread information block. + * + * Here's the layout your c library assumes: + * + * offset size description + * 0x0000 0x08 linear address pointer + * 0x0008 0x08 jmp_buf *exiter + * 0x0010 0x04 exit code + * 0x0030 0x08 linear address pointer + * 0x0038 0x04 tid + * 0x003c 0x04 errno + * */ privileged void *__initialize_tls(char tib[hasatleast 64]) { *(intptr_t *)tib = (intptr_t)tib; + *(intptr_t *)(tib + 0x08) = 0; + *(int *)(tib + 0x10) = -1; // exit code *(intptr_t *)(tib + 0x30) = (intptr_t)tib; + *(int *)(tib + 0x38) = -1; // tid *(int *)(tib + 0x3c) = __errno; return tib; } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index ba43b5123..b6efa27a1 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -68,7 +68,7 @@ relegated void __check_fail(const char *suffix, const char *opstr, } kprintf("%s\n", RESET); if (!IsTiny() && e == ENOMEM) { - PrintMemoryIntervals(2, &_mmi); + __print_maps(); } __die(); unreachable; diff --git a/libc/log/internal.h b/libc/log/internal.h index 14a644ca8..625da7514 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -10,11 +10,11 @@ COSMOPOLITAN_C_START_ extern hidden bool __nocolor; extern hidden int kCrashSigs[7]; extern hidden bool g_isrunningundermake; -extern hidden struct sigaction g_oldcrashacts[7]; void __start_fatal(const char *, int) hidden; void __oncrash(int, struct siginfo *, struct ucontext *) relegated; void __restore_tty(void); +void RestoreDefaultCrashSignalHandlers(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/leaks.c b/libc/log/leaks.c index 0f61dda6a..16c65ceee 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -108,7 +108,7 @@ noasan void CheckForMemoryLeaks(void) { } malloc_inspect_all(OnMemory, 0); kprintf("\n"); - PrintMemoryIntervals(2, &_mmi); + __print_maps(); /* PrintSystemMappings(2); */ /* PrintGarbage(); */ __restorewintty(); diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 325d0273a..35b26d413 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -35,6 +35,7 @@ #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/internal.h" #include "libc/runtime/pc.internal.h" +#include "libc/runtime/runtime.h" /** * @fileoverview Abnormal termination handling & GUI debugging. @@ -57,7 +58,6 @@ static const char kCpuFlags[12] forcealign(1) = "CVPRAKZSTIDO"; static const char kFpuExceptions[6] forcealign(1) = "IDZOUP"; int kCrashSigs[7]; -struct sigaction g_oldcrashacts[7]; relegated static void ShowFunctionCalls(ucontext_t *ctx) { struct StackFrame *bp; @@ -220,7 +220,7 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, ShowSseRegisters(ctx); } kprintf("\n"); - PrintMemoryIntervals(2, &_mmi); + __print_maps(); /* PrintSystemMappings(2); */ if (__argv) { for (i = 0; i < __argc; ++i) { @@ -232,16 +232,6 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, kprintf("\n"); } -relegated static void RestoreDefaultCrashSignalHandlers(void) { - size_t i; - sigset_t ss; - sigemptyset(&ss); - sigprocmask(SIG_SETMASK, &ss, NULL); - for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { - if (kCrashSigs[i]) sigaction(kCrashSigs[i], &g_oldcrashacts[i], NULL); - } -} - static wontreturn relegated noinstrument void __minicrash(int sig, struct siginfo *si, ucontext_t *ctx, diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 86efb79ad..29bdfeddf 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -16,13 +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/sigbits.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.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" @@ -31,12 +35,43 @@ STATIC_YOINK("__die"); /* for backtracing */ STATIC_YOINK("malloc_inspect_all"); /* for asan memory origin */ STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */ -static struct sigaltstack oldsigaltstack; extern const unsigned char __oncrash_thunks[8][11]; +static struct sigaltstack g_oldsigaltstack; +static struct sigaction g_oldcrashacts[7]; + +static void InstallCrashHandlers(int extraflags) { + size_t i; + struct sigaction sa; + bzero(&sa, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_NODEFER | extraflags; + sigfillset(&sa.sa_mask); + for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { + sigdelset(&sa.sa_mask, kCrashSigs[i]); + } + for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { + if (kCrashSigs[i]) { + sa.sa_sigaction = (sigaction_f)__oncrash_thunks[i]; + sigaction(kCrashSigs[i], &sa, &g_oldcrashacts[i]); + } + } +} + +relegated void RestoreDefaultCrashSignalHandlers(void) { + size_t i; + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK, &ss, NULL); + for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { + if (kCrashSigs[i]) { + sigaction(kCrashSigs[i], &g_oldcrashacts[i], NULL); + } + } +} static void FreeSigAltStack(void *p) { - sigaltstack(&oldsigaltstack, 0); - free(p); + InstallCrashHandlers(0); + sigaltstack(&g_oldsigaltstack, 0); + munmap(p, GetStackSize()); } /** @@ -57,8 +92,6 @@ static void FreeSigAltStack(void *p) { * @see callexitontermination() */ void ShowCrashReports(void) { - size_t i; - struct sigaction sa; struct sigaltstack ss; /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */ @@ -73,25 +106,17 @@ void ShowCrashReports(void) { bzero(&ss, sizeof(ss)); ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; - if ((ss.ss_sp = malloc(SIGSTKSZ))) { - if (!sigaltstack(&ss, &oldsigaltstack)) { + if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_STACK | MAP_ANONYMOUS, -1, 0))) { + if (!sigaltstack(&ss, &g_oldsigaltstack)) { __cxa_atexit(FreeSigAltStack, ss.ss_sp, 0); } else { - free(ss.ss_sp); + munmap(ss.ss_sp, GetStackSize()); } } - } - bzero(&sa, sizeof(sa)); - sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; - sigfillset(&sa.sa_mask); - for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { - sigdelset(&sa.sa_mask, kCrashSigs[i]); - } - for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { - if (kCrashSigs[i]) { - sa.sa_sigaction = (sigaction_f)__oncrash_thunks[i]; - sigaction(kCrashSigs[i], &sa, &g_oldcrashacts[i]); - } + InstallCrashHandlers(SA_ONSTACK); + } else { + InstallCrashHandlers(0); } GetSymbolTable(); } diff --git a/libc/nexgen32e/checkstackalign.S b/libc/nexgen32e/checkstackalign.S index e362d7854..e944f1b38 100644 --- a/libc/nexgen32e/checkstackalign.S +++ b/libc/nexgen32e/checkstackalign.S @@ -21,7 +21,7 @@ // Checks that stack is 16-byte aligned. // // This function crashes if called with a misaligned stack. -_checkstackalign: +CheckStackIsAligned: push %rbp mov %rsp,%rbp @@ -35,4 +35,4 @@ _checkstackalign: leave ret - .endfn _checkstackalign,globl + .endfn CheckStackIsAligned,globl diff --git a/libc/nexgen32e/nexgen32e.h b/libc/nexgen32e/nexgen32e.h index e9f7dacf0..c2d45ee3f 100644 --- a/libc/nexgen32e/nexgen32e.h +++ b/libc/nexgen32e/nexgen32e.h @@ -7,7 +7,7 @@ extern long kHalfCache3; void imapxlatab(void *); void insertionsort(int32_t *, size_t); -void _checkstackalign(void); +void CheckStackIsAligned(void); int64_t div10int64(int64_t) libcesque pureconst; int64_t div100int64(int64_t) libcesque pureconst; diff --git a/libc/nexgen32e/nt2sysv.S b/libc/nexgen32e/nt2sysv.S index ef5939d5b..34ae867d9 100644 --- a/libc/nexgen32e/nt2sysv.S +++ b/libc/nexgen32e/nt2sysv.S @@ -32,6 +32,8 @@ __nt2sysv: push %rbp mov %rsp,%rbp +// TODO(jart): We should probably find some way to use our own +// stack when Windows delivers signals ;_; .profilable sub $0x100,%rsp push %rbx diff --git a/libc/nt/enum/th32cs.h b/libc/nt/enum/th32cs.h new file mode 100644 index 000000000..9c870ecf0 --- /dev/null +++ b/libc/nt/enum/th32cs.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_TH32CS_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_TH32CS_H_ + +#define kNtTh32csInherit 0x80000000 +#define kNtTh32csSnapheaplist 0x00000001 +#define kNtTh32csSnapmodule 0x00000008 +#define kNtTh32csSnapmodule32 0x00000010 +#define kNtTh32csSnapprocess 0x00000002 +#define kNtTh32csSnapthread 0x00000004 + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_TH32CS_H_ */ diff --git a/libc/nt/kernel32/CreateToolhelp32Snapshot.s b/libc/nt/kernel32/CreateToolhelp32Snapshot.s index 54982f143..aea3d4177 100644 --- a/libc/nt/kernel32/CreateToolhelp32Snapshot.s +++ b/libc/nt/kernel32/CreateToolhelp32Snapshot.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" -.imp kernel32,__imp_CreateToolhelp32Snapshot,CreateToolhelp32Snapshot,250 +.imp kernel32,__imp_CreateToolhelp32Snapshot,CreateToolhelp32Snapshot,0 + + .text.windows +CreateToolhelp32Snapshot: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateToolhelp32Snapshot(%rip),%rax + jmp __sysv2nt + .endfn CreateToolhelp32Snapshot,globl + .previous diff --git a/libc/nt/kernel32/Process32FirstW.s b/libc/nt/kernel32/Process32FirstW.s index b8408bce5..d65af4e3f 100644 --- a/libc/nt/kernel32/Process32FirstW.s +++ b/libc/nt/kernel32/Process32FirstW.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" -.imp kernel32,__imp_Process32FirstW,Process32FirstW,1065 +.imp kernel32,__imp_Process32FirstW,Process32FirstW,0 + + .text.windows +Process32First: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_Process32FirstW(%rip),%rax + jmp __sysv2nt + .endfn Process32First,globl + .previous diff --git a/libc/nt/kernel32/Process32NextW.s b/libc/nt/kernel32/Process32NextW.s index aa49537b1..9ae46191c 100644 --- a/libc/nt/kernel32/Process32NextW.s +++ b/libc/nt/kernel32/Process32NextW.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" -.imp kernel32,__imp_Process32NextW,Process32NextW,1067 +.imp kernel32,__imp_Process32NextW,Process32NextW,0 + + .text.windows +Process32Next: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_Process32NextW(%rip),%rax + jmp __sysv2nt + .endfn Process32Next,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 091076566..a46644acf 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -208,7 +208,7 @@ imp 'CreateThreadpoolWait' CreateThreadpoolWait kernel32 0 imp 'CreateThreadpoolWork' CreateThreadpoolWork kernel32 0 imp 'CreateTimerQueue' CreateTimerQueue kernel32 0 imp 'CreateTimerQueueTimer' CreateTimerQueueTimer kernel32 0 -imp 'CreateToolhelp32Snapshot' CreateToolhelp32Snapshot kernel32 250 +imp 'CreateToolhelp32Snapshot' CreateToolhelp32Snapshot kernel32 0 2 imp 'CreateUmsCompletionList' CreateUmsCompletionList kernel32 251 imp 'CreateUmsThreadContext' CreateUmsThreadContext kernel32 252 imp 'CreateWaitableTimer' CreateWaitableTimerW kernel32 0 3 @@ -934,8 +934,8 @@ imp 'PowerSetRequest' PowerSetRequest kernel32 1059 imp 'PrefetchVirtualMemory' PrefetchVirtualMemory kernel32 0 4 imp 'PrepareTape' PrepareTape kernel32 1061 imp 'PrivMoveFileIdentity' PrivMoveFileIdentityW kernel32 1063 -imp 'Process32First' Process32FirstW kernel32 1065 -imp 'Process32Next' Process32NextW kernel32 1067 +imp 'Process32First' Process32FirstW kernel32 0 2 +imp 'Process32Next' Process32NextW kernel32 0 2 imp 'ProcessIdToSessionId' ProcessIdToSessionId kernel32 0 imp 'PssCaptureSnapshot' PssCaptureSnapshot kernel32 0 imp 'PssDuplicateSnapshot' PssDuplicateSnapshot kernel32 0 diff --git a/libc/nt/process.h b/libc/nt/process.h index 480e1b851..38745121e 100644 --- a/libc/nt/process.h +++ b/libc/nt/process.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_NT_PROCESS_H_ #define COSMOPOLITAN_LIBC_NT_PROCESS_H_ #include "libc/nt/startupinfo.h" +#include "libc/nt/struct/processentry32.h" #include "libc/nt/struct/processinformation.h" #include "libc/nt/struct/processmemorycounters.h" #include "libc/nt/struct/securityattributes.h" @@ -73,6 +74,10 @@ bool32 GetProcessMemoryInfo( int64_t hProcess, struct NtProcessMemoryCountersEx *out_ppsmemCounters, uint32_t cb); +int64_t CreateToolhelp32Snapshot(uint32_t dwFlags, uint32_t th32ProcessID); +bool32 Process32First(int64_t hSnapshot, struct NtProcessEntry32 *in_out_lppe); +bool32 Process32Next(int64_t hSnapshot, struct NtProcessEntry32 *out_lppe); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/process.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/nt/struct/processentry32.h b/libc/nt/struct/processentry32.h new file mode 100644 index 000000000..e6b481573 --- /dev/null +++ b/libc/nt/struct/processentry32.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_PROCESSENTRY32_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_PROCESSENTRY32_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +struct NtProcessEntry32 { + uint32_t dwSize; + uint32_t cntUsage; /* unused */ + uint32_t th32ProcessID; + uint64_t th32DefaultHeapID; /* unused */ + uint32_t th32ModuleID; /* unused */ + uint32_t cntThreads; + uint32_t th32ParentProcessID; + int32_t cPriClassBase; + uint32_t dwFlags; /* unused */ + char16_t szExeFile[260]; +}; + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_PROCESSENTRY32_H_ */ diff --git a/libc/runtime/clone.greg.c b/libc/runtime/clone.c similarity index 68% rename from libc/runtime/clone.greg.c rename to libc/runtime/clone.c index 02af5b940..5a7b87b37 100644 --- a/libc/runtime/clone.greg.c +++ b/libc/runtime/clone.c @@ -24,7 +24,6 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" #include "libc/nexgen32e/threaded.h" #include "libc/nt/runtime.h" @@ -50,6 +49,9 @@ STATIC_YOINK("gettid"); // for kprintf() #define LWP_DETACHED 0x00000040 #define LWP_SUSPENDED 0x00000080 +__msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue; +__msabi extern typeof(ExitThread) *const __imp_ExitThread; + struct CloneArgs { union { int tid; @@ -57,12 +59,11 @@ struct CloneArgs { int64_t tid64; }; int lock; - int flags; int *ctid; + int *ztid; char *tls; int (*func)(void *); void *arg; - void *pad; // TODO: Why does FreeBSD clobber this? }; struct __tfork { @@ -76,31 +77,33 @@ static char tibdefault[64]; //////////////////////////////////////////////////////////////////////////////// // THE NEW TECHNOLOGY -uint32_t WinThreadThunk(void *warg); -asm(".section\t.text.windows,\"ax\",@progbits\n\t" - ".local\tWinThreadThunk\n" - "WinThreadThunk:\n\t" - "xor\t%ebp,%ebp\n\t" - "mov\t%rcx,%rdi\n\t" - "mov\t%rcx,%rsp\n\t" - "and\t$-16,%rsp\n\t" - "push\t%rax\n\t" - "jmp\tWinThreadMain\n\t" - ".size\tWinThreadThunk,.-WinThreadThunk\n\t" - ".previous"); -__attribute__((__used__, __no_reorder__)) +int WinThreadLaunch(void *arg, int (*func)(void *), intptr_t rsp); -static textwindows wontreturn void -WinThreadMain(struct CloneArgs *wt) { +// we can't log this function because: +// 1. windows owns the backtrace pointer right now +// 2. ftrace unwinds rbp to determine depth +// 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 +noasan noinstrument static textwindows wontreturn void WinThreadEntry( + int rdi, int rsi, int rdx, struct CloneArgs *wt) { int rc; - if (wt->flags & CLONE_SETTLS) { - TlsSetValue(__tls_index, wt->tls); + if (wt->tls) { + asm("mov\t%1,%%gs:%0" + : "=m"(*((long *)0x1480 + __tls_index)) + : "r"(wt->tls)); } - if (wt->flags & CLONE_CHILD_SETTID) { - *wt->ctid = wt->tid; - } - rc = wt->func(wt->arg); - _Exit1(rc); + *wt->ctid = wt->tid; + rc = WinThreadLaunch(wt->arg, wt->func, (intptr_t)wt & -16); + // 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; + // since we didn't indirect this function through NT2SYSV() it's not + // safe to simply return, and as such, we just call ExitThread(). + __imp_ExitThread(rc); + unreachable; } static textwindows int CloneWindows(int (*func)(void *), char *stk, @@ -111,12 +114,12 @@ static textwindows int CloneWindows(int (*func)(void *), char *stk, wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->flags = flags; - wt->ctid = ctid; + wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; + wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->func = func; wt->arg = arg; - wt->tls = tls; - if ((h = CreateThread(0, 0, WinThreadThunk, wt, 0, &wt->utid))) { + wt->tls = flags & CLONE_SETTLS ? tls : 0; + if ((h = CreateThread(0, 0, (void *)WinThreadEntry, wt, 0, &wt->utid))) { CloseHandle(h); return wt->tid; } else { @@ -128,7 +131,7 @@ static textwindows int CloneWindows(int (*func)(void *), char *stk, // XNU'S NOT UNIX void XnuThreadThunk(void *pthread, int machport, void *(*func)(void *), - void *arg, intptr_t *stack, unsigned flags); + void *arg, intptr_t *stack, unsigned xnuflags); asm(".local\tXnuThreadThunk\n" "XnuThreadThunk:\n\t" "xor\t%ebp,%ebp\n\t" @@ -141,11 +144,11 @@ __attribute__((__used__, __no_reorder__)) static wontreturn void XnuThreadMain(void *pthread, int tid, int (*func)(void *arg), void *arg, - struct CloneArgs *wt, unsigned flags) { + struct CloneArgs *wt, unsigned xnuflags) { int ax; wt->tid = tid; _spunlock(&wt->lock); - if (wt->flags & CLONE_SETTLS) { + if (wt->tls) { // XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the // Go team at Google that they Apply stands by our ability to use it // https://github.com/golang/go/issues/23617#issuecomment-376662373 @@ -154,10 +157,21 @@ XnuThreadMain(void *pthread, int tid, int (*func)(void *arg), void *arg, : "0"(__NR_thread_fast_set_cthread_self), "D"(wt->tls - 0x30) : "rcx", "r11", "memory", "cc"); } - if (wt->flags & CLONE_CHILD_SETTID) { - *wt->ctid = tid; - } - _Exit1(func(arg)); + *wt->ctid = tid; + func(arg); + // we no longer use the stack after this point + // %rax = int bsdthread_terminate(%rdi = void *stackaddr, + // %rsi = size_t freesize, + // %rdx = uint32_t port, + // %r10 = uint32_t sem); + asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 + "xor\t%%r10d,%%r10d\n\t" // sem = 0 + "syscall\n\t" // _Exit1() + "ud2" + : "=m"(*wt->ztid) + : "a"(0x2000000 | 361), "D"(0), "S"(0), "d"(0) + : "rcx", "r10", "r11", "memory"); + unreachable; } static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, @@ -180,9 +194,9 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->flags = flags; - wt->ctid = ctid; - wt->tls = tls; + wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; + wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; + wt->tls = flags & CLONE_SETTLS ? tls : 0; _seizelock(&wt->lock); // TODO: How can we get the tid without locking? if ((rc = bsdthread_create(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU)) != -1) { _spinlock(&wt->lock); @@ -194,23 +208,18 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, //////////////////////////////////////////////////////////////////////////////// // FREE BESIYATA DISHMAYA -void FreebsdThreadThunk(void *) wontreturn; -asm(".local\tFreebsdThreadThunk\n" - "FreebsdThreadThunk:\n\t" - "xor\t%ebp,%ebp\n\t" - "mov\t%rdi,%rsp\n\t" - "and\t$-16,%rsp\n\t" - "push\t%rax\n\t" - "jmp\tFreebsdThreadMain\n\t" - ".size\tFreebsdThreadThunk,.-FreebsdThreadThunk"); -__attribute__((__used__, __no_reorder__)) - -static wontreturn void -FreebsdThreadMain(struct CloneArgs *wt) { - if (wt->flags & CLONE_CHILD_SETTID) { - *wt->ctid = wt->tid; - } - _Exit1(wt->func(wt->arg)); +static wontreturn void FreebsdThreadMain(void *p) { + struct CloneArgs *wt = p; + *wt->ctid = wt->tid; + wt->func(wt->arg); + // 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 + "syscall" // _Exit1() + : "=m"(*wt->ztid) + : "a"(431), "D"(0) + : "rcx", "r11", "memory"); + unreachable; } static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, @@ -222,16 +231,16 @@ static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags, wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->flags = flags; - wt->ctid = ctid; + wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; + wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->tls = tls; wt->func = func; wt->arg = arg; struct thr_param params = { - .start_func = FreebsdThreadThunk, + .start_func = FreebsdThreadMain, .arg = wt, .stack_base = stk, - .stack_size = stksz, + .stack_size = (((intptr_t)wt - (intptr_t)stk) & -16) - 8, .tls_base = flags & CLONE_SETTLS ? tls : 0, .tls_size = flags & CLONE_SETTLS ? tlssz : 0, .child_tid = &wt->tid64, @@ -277,7 +286,15 @@ __attribute__((__used__, __no_reorder__)) static privileged wontreturn void OpenbsdThreadMain(struct CloneArgs *wt) { - _Exit1(wt->func(wt->arg)); + wt->func(wt->arg); + // we no longer use the stack after this point + // void __threxit(%rdi = int32_t *notdead); + asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 + "syscall" // _Exit1() + : "=m"(*wt->ztid) + : "a"(302), "D"(0) + : "rcx", "r11", "memory"); + unreachable; } static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, @@ -288,8 +305,8 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->flags = flags; - wt->ctid = ctid; + wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; + wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->func = func; wt->arg = arg; params.tf_stack = wt; @@ -306,12 +323,19 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, // NET BESIYATA DISHMAYA static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg), - int *tid, int *ctid, int flags) { - int rc; - if (flags & CLONE_CHILD_SETTID) { - *ctid = *tid; - } - _Exit1(func(arg)); + int *tid, int *ctid, int *ztid) { + int ax, dx; + *ctid = *tid; + func(arg); + // 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 + "syscall\n\t" // _Exit1() + "ud2" + : "=a"(ax), "=d"(dx), "=m"(*ztid) + : "0"(310) + : "rcx", "r11", "memory"); + unreachable; } static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, @@ -325,8 +349,10 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, intptr_t dx, sp; static bool once; static int broken; - struct ucontext_netbsd *ctx; + struct ucontext_netbsd ctx; static struct ucontext_netbsd netbsd_clone_template; + + // memoize arbitrary valid processor state structure if (!once) { asm volatile(CFLAG_ASM("syscall") : CFLAG_CONSTRAINT(failed), "=a"(ax) @@ -343,31 +369,34 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, } sp = (intptr_t)(stk + stksz); sp -= sizeof(int); + sp = sp & -alignof(int); tid = (int *)sp; - sp -= sizeof(*ctx); - sp = sp & -alignof(*ctx); - ctx = (struct ucontext_netbsd *)sp; - memcpy(ctx, &netbsd_clone_template, sizeof(*ctx)); - ctx->uc_link = 0; - ctx->uc_mcontext.rbp = 0; - ctx->uc_mcontext.rsp = sp; - ctx->uc_mcontext.rip = (intptr_t)NetbsdThreadMain; - ctx->uc_mcontext.rdi = (intptr_t)arg; - ctx->uc_mcontext.rsi = (intptr_t)func; - ctx->uc_mcontext.rdx = (intptr_t)tid; - ctx->uc_mcontext.rcx = (intptr_t)ctid; - ctx->uc_mcontext.r8 = flags; - ctx->uc_flags |= _UC_STACK; - ctx->uc_stack.ss_sp = stk; - ctx->uc_stack.ss_size = stksz; - ctx->uc_stack.ss_flags = 0; + sp = sp & -16; + sp -= 8; + // pass parameters in process state + memcpy(&ctx, &netbsd_clone_template, sizeof(ctx)); + ctx.uc_link = 0; + ctx.uc_mcontext.rbp = 0; + ctx.uc_mcontext.rsp = sp; + ctx.uc_mcontext.rip = (intptr_t)NetbsdThreadMain; + ctx.uc_mcontext.rdi = (intptr_t)arg; + ctx.uc_mcontext.rsi = (intptr_t)func; + ctx.uc_mcontext.rdx = (intptr_t)tid; + ctx.uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid); + ctx.uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid); + ctx.uc_flags |= _UC_STACK; + ctx.uc_stack.ss_sp = stk; + ctx.uc_stack.ss_size = stksz; + ctx.uc_stack.ss_flags = 0; if (flags & CLONE_SETTLS) { - ctx->uc_flags |= _UC_TLSBASE; - ctx->uc_mcontext._mc_tlsbase = (intptr_t)tls; + ctx.uc_flags |= _UC_TLSBASE; + ctx.uc_mcontext._mc_tlsbase = (intptr_t)tls; } + + // perform the system call asm volatile(CFLAG_ASM("syscall") : CFLAG_CONSTRAINT(failed), "=a"(ax), "=d"(dx) - : "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid) + : "1"(__NR__lwp_create), "D"(&ctx), "S"(LWP_DETACHED), "2"(tid) : "rcx", "r11", "memory"); if (!failed) { return *tid; @@ -388,6 +417,12 @@ int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, int ax; intptr_t *stack = (intptr_t *)(stk + stksz); *--stack = (intptr_t)arg; + // %rax = syscall(%rax = __NR_clone, + // %rdi = flags, + // %rsi = child_stack, + // %rdx = parent_tidptr, + // %r10 = child_tidptr, + // %r8 = new_tls); asm volatile("mov\t%4,%%r10\n\t" // ctid "mov\t%5,%%r8\n\t" // tls "mov\t%6,%%r9\n\t" // func @@ -398,10 +433,11 @@ int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, "pop\t%%rdi\n\t" // arg "call\t*%%r9\n\t" // func "xchg\t%%eax,%%edi\n\t" - "jmp\t_Exit1\n1:" + "mov\t$0x3c,%%eax\n\t" + "syscall\n1:" : "=a"(ax) : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "g"(ctid), - "g"(tls), "g"(func) + "g"(tls), "g"(func), "d"(ptid) : "rcx", "r8", "r9", "r10", "r11", "memory"); if (ax > -4096u) errno = -ax, ax = -1; return ax; @@ -419,8 +455,7 @@ int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, * function should be synchronized using shared memory operations. * * Any memory that's required by this system call wrapper is allocated - * to the top of your stack. This is normally about 64 bytes, although - * on NetBSD it's currently 800. + * to the top of your stack. This shouldn't be more than 128 bytes. * * Your function is called from within the stack you specify. A return * address is pushed onto your stack, that causes returning to jump to @@ -464,9 +499,25 @@ int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, * and it's advised to have the bottom-most page, be a guard page * @param flags should have: * - `CLONE_THREAD|CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` - * and may optionally bitwise any of the following: - * - `CLONE_CHILD_SETTID` is needed too if you use `ctid` - * - `CLONE_SETTLS` is needed too if you set `tls` + * and you may optionally bitwise or any of the following: + * - `CLONE_CHILD_SETTID` is needed too if you use `ctid` which + * is part of the memory the child owns and it'll be set right + * before the callback function is invoked + * - `CLONE_CHILD_CLEARTID` causes `*ctid = 0` upon termination + * which can be used to implement join so that the parent may + * safely free the stack memory that the child is using + * - `CLONE_PARENT_SETTID` is needed too if you use `ptid` and this + * is guaranteed to happen before clone() returns + * - `CLONE_SETTLS` is needed too if you set `tls`. You may get this + * value from the thread by calling __get_tls(). There are a few + * layout expectations imposed by your C library. Those are all + * documented by __initialize_tls() which initializes the parts of + * the first 64 bytes of tls memory that libc cares about. Also + * note that if you decide to use tls once then you must use it + * for everything, since this flag also flips a runtime state that + * enables it for the main thread and functions such as + * __errno_location() will begin assuming they can safely access + * the tls segment register. * @param arg will be passed to your callback * @param tls may be used to set the thread local storage segment; * this parameter is ignored if `CLONE_SETTLS` is not set @@ -506,10 +557,12 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, rc = einval(); } else if (IsLinux()) { rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); - } else if (!IsTiny() && (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | - CLONE_CHILD_SETTID)) != - (CLONE_THREAD | CLONE_VM | CLONE_FS | - CLONE_FILES | CLONE_SIGHAND)) { + } else if (!IsTiny() && + (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) != + (CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND)) { + STRACE("clone flag unsupported on this platform"); rc = einval(); } else if (IsXnu()) { rc = CloneXnu(func, stk, stksz, flags, arg, tls, tlssz, ctid); @@ -525,7 +578,12 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, rc = enosys(); } - STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d", func, stk, + if (rc != -1 && (flags & CLONE_PARENT_SETTID)) { + *ptid = rc; + } + + STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d% m", func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid, rc); + return rc; } diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index c3494741a..feb47416f 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -17,9 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" #include "libc/fmt/itoa.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchgp.h" #include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" @@ -49,7 +51,7 @@ void ftrace_hook(void); -_Alignas(64) char ftrace_lock; +_Alignas(64) int ftrace_lock; static struct Ftrace { int skew; @@ -75,6 +77,32 @@ static privileged int GetNestingLevel(struct StackFrame *frame) { return MIN(MAX_NESTING, nesting); } +static privileged inline void ReleaseFtraceLock(void) { + int zero = 0; + __atomic_store(&ftrace_lock, &zero, __ATOMIC_RELAXED); +} + +static privileged inline bool AcquireFtraceLock(void) { + int me, owner, tries; + for (tries = 0, me = gettid();;) { + owner = 0; + if (_lockcmpxchgp(&ftrace_lock, &owner, me)) { + return true; + } + if (owner == me) { + // we ignore re-entry into ftrace. while the code and build config + // is written to make re-entry highly unlikely, it's impossible to + // guarantee. there's also the possibility of asynchronous signals + return false; + } + if (++tries & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } + } +} + /** * Prints name of function being called. * @@ -83,21 +111,22 @@ static privileged int GetNestingLevel(struct StackFrame *frame) { * according to the System Five NexGen32e ABI. */ privileged void ftracer(void) { + long stackuse; uint64_t stamp; - size_t stackuse; struct StackFrame *frame; - _spinlock_cooperative(&ftrace_lock); - stamp = rdtsc(); - frame = __builtin_frame_address(0); - frame = frame->next; - if (frame->addr != g_ftrace.lastaddr) { - stackuse = ROUNDUP((intptr_t)frame, GetStackSize()) - (intptr_t)frame; - kprintf("%rFUN %5P %'13T %'*lu %*s%t\r\n", g_ftrace.stackdigs, stackuse, - GetNestingLevel(frame) * 2, "", frame->addr); - g_ftrace.laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); - g_ftrace.lastaddr = frame->addr; + if (AcquireFtraceLock()) { + stamp = rdtsc(); + frame = __builtin_frame_address(0); + frame = frame->next; + if (frame->addr != g_ftrace.lastaddr) { + stackuse = (intptr_t)GetStackAddr(0) + GetStackSize() - (intptr_t)frame; + kprintf("%rFUN %5P %'13T %'*ld %*s%t\r\n", g_ftrace.stackdigs, stackuse, + GetNestingLevel(frame) * 2, "", frame->addr); + g_ftrace.laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); + g_ftrace.lastaddr = frame->addr; + } + ReleaseFtraceLock(); } - _spunlock(&ftrace_lock); } textstartup int ftrace_install(void) { diff --git a/libc/runtime/getsymboltable.greg.c b/libc/runtime/getsymboltable.c similarity index 88% rename from libc/runtime/getsymboltable.greg.c rename to libc/runtime/getsymboltable.c index 38e54f0d4..fa0358e36 100644 --- a/libc/runtime/getsymboltable.greg.c +++ b/libc/runtime/getsymboltable.c @@ -31,7 +31,7 @@ #include "libc/zipos/zipos.internal.h" static char g_lock; -static struct SymbolTable *g_symtab; +hidden struct SymbolTable *__symtab; // for kprintf /** * Looks for `.symtab` in zip central directory. @@ -70,6 +70,7 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { memcpy(res, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), size); break; #if 0 + // TODO(jart): fix me case kZipCompressionDeflate: rc = undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), GetZipLfileCompressedSize(zipos->map + lf), &ds); @@ -121,21 +122,21 @@ static struct SymbolTable *GetSymbolTableFromElf(void) { struct SymbolTable *GetSymbolTable(void) { struct Zipos *z; if (_trylock(&g_lock)) return 0; - if (!g_symtab && !__isworker) { + if (!__symtab && !__isworker) { if (weaken(__zipos_get) && (z = weaken(__zipos_get)())) { - if ((g_symtab = GetSymbolTableFromZip(z))) { - g_symtab->names = - (uint32_t *)((char *)g_symtab + g_symtab->names_offset); - g_symtab->name_base = - (char *)((char *)g_symtab + g_symtab->name_base_offset); + if ((__symtab = GetSymbolTableFromZip(z))) { + __symtab->names = + (uint32_t *)((char *)__symtab + __symtab->names_offset); + __symtab->name_base = + (char *)((char *)__symtab + __symtab->name_base_offset); } } - if (!g_symtab) { - g_symtab = GetSymbolTableFromElf(); + if (!__symtab) { + __symtab = GetSymbolTableFromElf(); } } _spunlock(&g_lock); - return g_symtab; + return __symtab; } /** @@ -144,11 +145,14 @@ struct SymbolTable *GetSymbolTable(void) { * @param t if null will be auto-populated only if already open * @return index or -1 if nothing found */ -privileged int __get_symbol(struct SymbolTable *t, intptr_t a) { - /* asan runtime depends on this function */ +noinstrument privileged int __get_symbol(struct SymbolTable *t, intptr_t a) { + // we need privileged because: + // kprintf is privileged and it depends on this + // we don't want function tracing because: + // function tracing depends on this function via kprintf unsigned l, m, r, n, k; - if (!t && g_symtab) { - t = g_symtab; + if (!t && __symtab) { + t = __symtab; } if (t) { l = 0; diff --git a/libc/runtime/memtrack.greg.c b/libc/runtime/memtrack.greg.c index e4f58c759..9ea60d1e1 100644 --- a/libc/runtime/memtrack.greg.c +++ b/libc/runtime/memtrack.greg.c @@ -39,7 +39,7 @@ static void *MoveMemoryIntervals(struct MemoryInterval *d, const struct MemoryInterval *s, int n) { - /* asan runtime depends on this function */ + // asan runtime depends on this function int i; assert(n >= 0); if (d > s) { @@ -55,7 +55,7 @@ static void *MoveMemoryIntervals(struct MemoryInterval *d, } static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) { - /* asan runtime depends on this function */ + // asan runtime depends on this function assert(i >= 0); assert(i + n <= mm->i); MoveMemoryIntervals(mm->p + i, mm->p + i + n, mm->i - (i + n)); @@ -71,7 +71,7 @@ static bool ExtendMemoryIntervals(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() */ + // TODO(jart): These map handles should not leak across NT fork() if (mm->p == mm->s) { if (IsAsan()) { shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000); @@ -100,7 +100,7 @@ static bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { } int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { - /* asan runtime depends on this function */ + // asan runtime depends on this function int rc; rc = 0; assert(i >= 0); @@ -192,7 +192,7 @@ int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, int prot, int flags, bool readonlyfile, bool iscow, long offset, long size) { - /* asan runtime depends on this function */ + // asan runtime depends on this function unsigned i; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 72c3fb3e3..43e9efacd 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #define COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #include "libc/assert.h" +#include "libc/bits/midpoint.h" #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/nt/enum/version.h" @@ -168,7 +169,7 @@ forceinline unsigned FindMemoryInterval(const struct MemoryIntervals *mm, l = 0; r = mm->i; while (l < r) { - m = (l + r) >> 1; + m = _midpoint(l, r); if (mm->p[m].y < x) { l = m + 1; } else { diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index d9e0337ab..cb58ffb8c 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -68,57 +68,83 @@ static wontreturn void OnUnrecoverableMmapError(const char *s) { _Exit(199); } -noasan static bool IsMapped(char *p, size_t n) { - return OverlapsImageSpace(p, n) || IsMemtracked(FRAME(p), FRAME(p + (n - 1))); +static noasan inline bool OverlapsExistingMapping(char *p, size_t n) { + int a, b, i; + assert(n > 0); + a = FRAME(p); + b = FRAME(p + (n - 1)); + i = FindMemoryInterval(&_mmi, a); + if (i < _mmi.i) { + if (a <= _mmi.p[i].x && _mmi.p[i].x <= b) return true; + if (a <= _mmi.p[i].y && _mmi.p[i].y <= b) return true; + if (_mmi.p[i].x <= a && b <= _mmi.p[i].y) return true; + } + return false; } -noasan static bool NeedAutomap(char *p, size_t n) { - return !p || OverlapsArenaSpace(p, n) || OverlapsShadowSpace(p, n) || - IsMapped(p, n); -} - -noasan static bool ChooseMemoryInterval(int x, int n, int *res) { - int i; +static noasan bool ChooseMemoryInterval(int x, int n, int align, int *res) { + int i, start, end; + assert(align > 0); if (_mmi.i) { + + // find the start of the automap memory region i = FindMemoryInterval(&_mmi, x); if (i < _mmi.i) { - if (x + n < _mmi.p[i].x) { - *res = x; - return true; + + // check to see if there's space available before the first entry + if (!__builtin_add_overflow(x, align - 1, &start)) { + start &= -align; + if (!__builtin_add_overflow(start, n - 1, &end)) { + if (end < _mmi.p[i].x) { + *res = start; + return true; + } + } } + + // check to see if there's space available between two entries while (++i < _mmi.i) { - if (_mmi.p[i].x - _mmi.p[i - 1].y > n) { - *res = _mmi.p[i - 1].y + 1; - return true; + if (!__builtin_add_overflow(_mmi.p[i - 1].y, 1, &start) && + !__builtin_add_overflow(start, align - 1, &start)) { + start &= -align; + if (!__builtin_add_overflow(start, n - 1, &end)) { + if (end < _mmi.p[i].x) { + *res = start; + return true; + } + } } } } - if (INT_MAX - _mmi.p[i - 1].y >= n) { - *res = _mmi.p[i - 1].y + 1; - return true; + + // otherwise append after the last entry if space is available + if (!__builtin_add_overflow(_mmi.p[i - 1].y, 1, &start) && + !__builtin_add_overflow(start, align - 1, &start)) { + start &= -align; + if (!__builtin_add_overflow(start, n - 1, &end)) { + *res = start; + return true; + } } - return false; + } else { - *res = x; - return true; + // if memtrack is empty, then just assign the requested address + // assuming it doesn't overflow + if (!__builtin_add_overflow(x, align - 1, &start)) { + start &= -align; + if (!__builtin_add_overflow(start, n - 1, &end)) { + *res = start; + return true; + } + } } + + return false; } -noasan static bool Automap(int n, int *res) { - *res = -1; - if (ChooseMemoryInterval(FRAME(kAutomapStart), n, res)) { - assert(*res >= FRAME(kAutomapStart)); - if (*res + n <= FRAME(kAutomapStart + (kAutomapStart - 1))) { - return true; - } else { - STRACE("mmap(%.12p, %p) ENOMEM (automap interval exhausted)", ADDR(*res), - ADDR(n + 1)); - return false; - } - } else { - STRACE("mmap(%.12p, %p) ENOMEM (automap failed)", ADDR(*res), ADDR(n + 1)); - return false; - } +noasan static bool Automap(int count, int align, int *res) { + return ChooseMemoryInterval(FRAME(kAutomapStart), count, align, res) && + *res + count <= FRAME(kAutomapStart + (kAutomapSize - 1)); } noasan static size_t GetMemtrackSize(struct MemoryIntervals *mm) { @@ -221,21 +247,16 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, } #endif char *p = addr; - bool needguard; struct DirectMap dm; - size_t virtualused, virtualneed; int a, b, i, f, m, n, x; + bool needguard, clashes; + size_t virtualused, virtualneed; if (UNLIKELY(!size)) { STRACE("size=0"); return VIP(einval()); } - if (UNLIKELY(!IsLegalSize(size))) { - STRACE("size isn't 48-bit"); - return VIP(einval()); - } - if (UNLIKELY(!IsLegalPointer(p))) { STRACE("p isn't 48-bit"); return VIP(einval()); @@ -266,29 +287,28 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, return VIP(einval()); } - if (UNLIKELY(INT64_MAX - size < off)) { - STRACE("too large"); - return VIP(einval()); - } - if (UNLIKELY(!ALIGNED(off))) { STRACE("p isn't 64kb aligned"); return VIP(einval()); } - if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { -#ifdef SYSDEBUG - if (OverlapsImageSpace(p, size)) { - STRACE("overlaps image"); - } else { - STRACE("overlaps existing"); + if (fd == -1) { + size = ROUNDUP(size, FRAMESIZE); + if (IsWindows()) { + prot |= PROT_WRITE; /* kludge */ } -#endif - return VIP(efault()); + } else if (__isfdkind(fd, kFdZip)) { + STRACE("fd is zipos handle"); + return VIP(einval()); } - if (__isfdkind(fd, kFdZip)) { - STRACE("fd is zipos handle"); + if (UNLIKELY(!IsLegalSize(size))) { + STRACE("size isn't 48-bit"); + return VIP(einval()); + } + + if (UNLIKELY(INT64_MAX - size < off)) { + STRACE("too large"); return VIP(einval()); } @@ -301,15 +321,29 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, return VIP(enomem()); } - if (fd == -1) { - size = ROUNDUP(size, FRAMESIZE); - if (IsWindows()) { - prot |= PROT_WRITE; /* kludge */ - } + clashes = OverlapsImageSpace(p, size) || OverlapsExistingMapping(p, size); + + if ((flags & MAP_FIXED_NOREPLACE) && clashes) { + STRACE("noreplace overlaps existing"); + return VIP(eexist()); + } + + if (__builtin_add_overflow((int)(size >> 16), (int)!!(size & (FRAMESIZE - 1)), + &n)) { + STRACE("memory range overflows"); + return VIP(einval()); + } + + // if size is a two power then automap will use it as alignment + if (IS2POW(size)) { + a = size >> 16; + if (!a) { + a = 1; + } + } else { + a = 1; } - n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1)); - assert(n > 0); f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; if (flags & MAP_FIXED) { x = FRAME(p); @@ -318,10 +352,11 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); } } - } else if (!NeedAutomap(p, size)) { + } else if (p && !clashes && !OverlapsArenaSpace(p, size) && + !OverlapsShadowSpace(p, size)) { x = FRAME(p); - } else if (!Automap(n, &x)) { - STRACE("AUTOMAP OUT OF MEMORY D:"); + } else if (!Automap(n, a, &x)) { + STRACE("automap has no room for %d frames with %d alignment", n, a); return VIP(enomem()); } diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 168597e99..55e2f98f1 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -337,7 +337,7 @@ textstartup void __printargs(const char *prologue) { PRINT(""); PRINT("MEMTRACK"); - PrintMemoryIntervals(2, &_mmi); + __print_maps(); PRINT(""); PRINT("TERMIOS"); diff --git a/libc/runtime/printmaps.c b/libc/runtime/printmaps.c new file mode 100644 index 000000000..adba80522 --- /dev/null +++ b/libc/runtime/printmaps.c @@ -0,0 +1,30 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/spinlock.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" + +/** + * Prints memory mappings to stderr. + */ +void __print_maps(void) { + _spinlock(&_mmi.lock); + PrintMemoryIntervals(2, &_mmi); + _spunlock(&_mmi.lock); +} diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index a5b4d50cc..f486d4909 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -112,6 +112,7 @@ void __morph_begin(void); void __morph_end(void); unsigned char *GetFirstInstruction(void); unsigned char *GetInstructionLengths(void); +void __print_maps(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 63fc71036..43501f3a3 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -64,6 +64,7 @@ $(LIBC_RUNTIME_A).pkg: \ # this is the function tracing runtime o/$(MODE)/libc/runtime/ftracer.o: \ OVERRIDE_CFLAGS += \ + -x-no-pg \ -mno-fentry \ -ffreestanding \ -fno-sanitize=all @@ -86,8 +87,7 @@ 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/opensymboltable.o \ -o/$(MODE)/libc/runtime/getsymboltable.greg.o: \ +o/$(MODE)/libc/runtime/opensymboltable.o: \ OVERRIDE_CFLAGS += \ -Os \ -ffreestanding \ diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index 4e231d54a..88781ced6 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -12,6 +12,7 @@ * This defaults to `STACKSIZE`. The bottom-most page will be protected * to ensure your stack does not magically grow beyond this value. It's * possible to detect stack overflows, by calling `ShowCrashReports()`. + * Your stack size must be a power of two; the linker will check this. * * If you want to know how much stack your programs needs, then * @@ -28,11 +29,17 @@ /** * Tunes APE stack virtual address. * - * This defaults to `0x7e0000000000 - STACKSIZE`. The value defined by - * this macro will be respected, with two exceptions: (1) in MODE=tiny - * the operating system provided stack is used instead and (2) Windows - * Seven doesn't support 64-bit addresses so 0x10000000 - GetStackSize - * is used instead. + * This value must be aligned according to your stack size, and that's + * checked by your linker script. This defaults to `0x700000000000` so + * + * 1. It's easy to see how close you are to the bottom + * 2. The linker script error is unlikely to happen + * + * This macro will be respected, with two exceptions + * + * 1. In MODE=tiny the operating system provided stack is used instead + * 2. Windows 7 doesn't support 64-bit addresses, so we'll instead use + * `0x10000000 - GetStackSize()` as the stack address * * @see libc/sysv/systemfive.S * @see libc/nt/winmain.greg.c @@ -56,10 +63,20 @@ extern char ape_stack_prot[] __attribute__((__weak__)); extern char ape_stack_memsz[] __attribute__((__weak__)); extern char ape_stack_align[] __attribute__((__weak__)); +/** + * Returns size of stack, which is always a two power. + */ #define GetStackSize() ((uintptr_t)ape_stack_memsz) /** * Returns address of bottom of stack. + * + * This takes into consideration threads and sigaltstack. This is + * implemented as a fast pure expression, since we're able to make the + * assumption that stack sizes are two powers and aligned. This is + * thanks to (1) the linker script checks the statically chosen sizes, + * and (2) the mmap() address picker will choose aligned addresses when + * the provided size is a two power. */ #define GetStackAddr(ADDEND) \ ((void *)((((intptr_t)__builtin_frame_address(0) - 1) & -GetStackSize()) + \ @@ -67,6 +84,12 @@ extern char ape_stack_align[] __attribute__((__weak__)); /** * Returns preferred bottom address of stack. + * + * This is the stakc address of the main process. The only time that + * isn't guaranteed to be the case is in MODE=tiny, since it doesn't + * link the code for stack creation at startup. This generally isn't + * problematic, since MODE=tiny doesn't use any of the runtime codes + * which want the stack to be cheaply knowable, e.g. ftrace, kprintf */ #define GetStaticStackAddr(ADDEND) \ ({ \ diff --git a/libc/runtime/winthreadlaunch.S b/libc/runtime/winthreadlaunch.S new file mode 100644 index 000000000..fc86864be --- /dev/null +++ b/libc/runtime/winthreadlaunch.S @@ -0,0 +1,46 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ 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 arg +// @param %rsi is func +// @param %rdx is stack +// @return %rax is exit code +// @see clone() +WinThreadLaunch: + push %rbx + push %r15 + mov %rbp,%r15 + mov %rsp,%rbx + mov %rdx,%rsp + xor %rbp,%rbp + call *%rsi + mov %r15,%rbp + mov %rbx,%rsp + pop %r15 + pop %rbx + ret + .endfn WinThreadLaunch,globl,hidden diff --git a/libc/stdio/unlocked/stdio_unlock.S b/libc/stdio/unlocked/stdio_unlock.S index 49f11ce44..dd25128a2 100644 --- a/libc/stdio/unlocked/stdio_unlock.S +++ b/libc/stdio/unlocked/stdio_unlock.S @@ -38,6 +38,7 @@ stdio_unlock: push %rbp mov %rsp,%rbp + .profilable // acquires mutex push %rcx diff --git a/libc/str/str.mk b/libc/str/str.mk index 8b2a34226..813af6569 100644 --- a/libc/str/str.mk +++ b/libc/str/str.mk @@ -86,6 +86,14 @@ o/$(MODE)/libc/str/windowstimetotimespec.o: \ OVERRIDE_CFLAGS += \ -O2 +# we can't use compiler magic because: +# kprintf() depends on these functions +o/$(MODE)/libc/fmt/strsignal.greg.o: \ + OVERRIDE_CFLAGS += \ + -fpie \ + -ffreestanding \ + $(NO_MAGIC) + LIBC_STR_LIBS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x))) LIBC_STR_SRCS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_SRCS)) LIBC_STR_HDRS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/str/strsignal.greg.c b/libc/str/strsignal.greg.c index a349ac47c..b169c1264 100644 --- a/libc/str/strsignal.greg.c +++ b/libc/str/strsignal.greg.c @@ -35,7 +35,9 @@ static char g_strsignal[12]; * @return pointer to static memory that mutates on subsequent calls * @see sigaction() */ -noasan noinstrument char *strsignal(int sig) { +privileged char *strsignal(int sig) { + // we need privileged because: + // kprintf is privileged and it depends on this char *p; const char *s; p = g_strsignal; diff --git a/libc/testlib/quota.c b/libc/testlib/quota.c index 815e7e994..a9e452a66 100644 --- a/libc/testlib/quota.c +++ b/libc/testlib/quota.c @@ -32,7 +32,7 @@ #include "libc/testlib/testlib.h" #include "third_party/dlmalloc/dlmalloc.h" -static noasan relegated uint64_t CountMappedBytes(void) { +static noasan noubsan relegated uint64_t CountMappedBytes(void) { size_t i; uint64_t x, y; for (x = i = 0; i < _mmi.i; ++i) { @@ -77,7 +77,7 @@ relegated void __oom_hook(size_t request) { kprintf("FIX CODE OR TUNE QUOTA += -M%dm\n", newlim / (1024 * 1024)); } kprintf("\n"); - PrintMemoryIntervals(2, &_mmi); + __print_maps(); kprintf("\nTHE STRAW THAT BROKE THE CAMEL'S BACK\n"); PrintBacktraceUsingSymbols(2, 0, GetSymbolTable()); PrintSystemMappings(2); diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 2cece08e6..4f09954e0 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -35,7 +35,7 @@ struct sigaction oldsa; volatile bool gotsigint; void OnSigInt(int sig) { - _checkstackalign(); + CheckStackIsAligned(); gotsigint = true; } @@ -109,7 +109,7 @@ TEST(sigaction, testPingPongParentChildWithSigint) { volatile int trapeax; void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) { - _checkstackalign(); + CheckStackIsAligned(); trapeax = ctx->uc_mcontext.rax; } @@ -126,7 +126,7 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) { // test signal handler can modify cpu registers (now it's recoverable!) void SkipOverFaultingInstruction(struct ucontext *ctx) { - _checkstackalign(); + CheckStackIsAligned(); struct XedDecodedInst xedd; xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); @@ -134,7 +134,7 @@ void SkipOverFaultingInstruction(struct ucontext *ctx) { } void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { - _checkstackalign(); + CheckStackIsAligned(); SkipOverFaultingInstruction(ctx); ctx->uc_mcontext.rax = 42; ctx->uc_mcontext.rdx = 0; diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index e283d19cc..9c87717bc 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -702,6 +702,7 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WTERMSIG(ws)); EXPECT_EQ(IsAsan() ? 77 : 128 + SIGSEGV, WEXITSTATUS(ws)); /* NULL is stopgap until we can copy symbol tables into binary */ if (!strstr(output, IsAsan() ? "null pointer" : "Uncaught SIGSEGV (SEGV_")) { diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index 7311543a4..f57b7d516 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -38,9 +38,8 @@ #include "libc/time/time.h" #define THREADS 8 -#define ENTRIES 256 +#define ENTRIES 1024 -char locks[THREADS]; _Atomic(bool) ready; volatile uint64_t A[THREADS * ENTRIES]; @@ -49,7 +48,7 @@ void OnChld(int sig) { } dontinline void Pause(void) { - __builtin_ia32_pause(); + // when ftrace is enabled } dontinline void Generate(int i) { @@ -62,7 +61,6 @@ int Thrasher(void *arg) { for (i = 0; i < ENTRIES; ++i) { Generate(id * ENTRIES + i); } - _spunlock(locks + id); return 0; } @@ -93,24 +91,22 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { sigemptyset(&ss); sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); - for (i = 0; i < THREADS; ++i) { - locks[i] = 1; - } ready = false; + memset(tid, -1, sizeof(tid)); for (i = 0; i < THREADS; ++i) { - tls[i] = calloc(1, 64); - __initialize_tls(tls[i]); + tls[i] = __initialize_tls(calloc(1, 64)); stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); - tid[i] = - clone(Thrasher, stacks[i], GetStackSize(), - CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, - (void *)(intptr_t)i, 0, tls[i], 64, 0); - ASSERT_NE(-1, tid[i]); + ASSERT_NE(-1, clone(Thrasher, stacks[i], GetStackSize(), + CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_CLEARTID, + (void *)(intptr_t)i, 0, tls[i], 64, tid + i)); } ready = true; for (i = 0; i < THREADS; ++i) { - _spinlock(locks + i); + _spinlock(tid + i); + EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize())); + free(tls[i]); } sigaction(SIGCHLD, &oldsa, 0); sigprocmask(SIG_BLOCK, &oldss, 0); @@ -121,10 +117,4 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { EXPECT_NE(A[i], A[j], "i=%d j=%d", i, j); } } - for (i = 0; i < THREADS; ++i) { - EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize())); - } - for (i = 0; i < THREADS; ++i) { - free(tls[i]); - } } diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index 4f4c4c409..232d5a48a 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -21,10 +21,12 @@ #include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" +#include "libc/log/backtrace.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/nexgen32e/threaded.h" #include "libc/runtime/stack.h" +#include "libc/runtime/symbols.internal.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" @@ -34,14 +36,11 @@ #include "libc/time/time.h" char *stack, *tls; -int x, me, tid, thechilde; -_Alignas(64) volatile char lock; +int x, me, tid, thechilde, childetid; void SetUp(void) { x = 0; - lock = 0; me = gettid(); - thechilde = 0; tls = calloc(1, 64); __initialize_tls(tls); *(int *)(tls + 0x3c) = 31337; @@ -50,11 +49,26 @@ void SetUp(void) { } void TearDown(void) { + EXPECT_SYS(0, 0, munmap(stack, GetStackSize())); free(tls); } +int DoNothing(void *arg) { + CheckStackIsAligned(); + return 0; +} + int CloneTest1(void *arg) { - _checkstackalign(); + intptr_t rsp, top, bot; + CheckStackIsAligned(); + rsp = (intptr_t)__builtin_frame_address(0); + bot = (intptr_t)stack; + 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; if (!IsWindows()) { ASSERT_EQ(31337, errno); @@ -65,22 +79,21 @@ int CloneTest1(void *arg) { ASSERT_EQ(23, (intptr_t)arg); thechilde = gettid(); ASSERT_NE(gettid(), getpid()); - _spunlock(&lock); - return 0; -} - -int DoNothing(void *arg) { - _checkstackalign(); + ASSERT_EQ(gettid(), childetid); // CLONE_CHILD_SETTID return 0; } TEST(clone, test1) { - _spinlock(&lock); + int ptid = 0; + _seizelock(&childetid); ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_SETTLS, - (void *)23, 0, tls, 64, 0))); - _spinlock(&lock); + CLONE_SIGHAND | CLONE_PARENT_SETTID | + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | + CLONE_SETTLS, + (void *)23, &ptid, tls, 64, &childetid))); + _spinlock(&childetid); // CLONE_CHILD_CLEARTID + ASSERT_EQ(tid, ptid); ASSERT_EQ(42, x); ASSERT_NE(me, tid); ASSERT_EQ(tid, thechilde); @@ -91,24 +104,24 @@ TEST(clone, test1) { } int CloneTestSys(void *arg) { - _checkstackalign(); + CheckStackIsAligned(); thechilde = gettid(); ASSERT_EQ(31337, errno); open(0, 0); ASSERT_EQ(EFAULT, errno); - _spunlock(&lock); return 0; } TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { ASSERT_EQ(0, errno); ASSERT_EQ(31337, *(int *)(tls + 0x3c)); - _spinlock(&lock); + _seizelock(&childetid); ASSERT_NE(-1, (tid = clone(CloneTestSys, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_SETTLS, - (void *)23, 0, tls, 64, 0))); - _spinlock(&lock); + CLONE_SIGHAND | CLONE_CHILD_SETTID | + CLONE_CHILD_CLEARTID | CLONE_SETTLS, + (void *)23, 0, tls, 64, &childetid))); + _spinlock(&childetid); // CLONE_CHILD_CLEARTID ASSERT_EQ(0, errno); ASSERT_EQ(EFAULT, *(int *)(tls + 0x3c)); } diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 046eba15d..285581aa2 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -22,8 +22,8 @@ #include "libc/calls/calls.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/fmt/fmt.h" -#include "libc/intrin/kprintf.h" #include "libc/linux/mmap.h" #include "libc/linux/munmap.h" #include "libc/log/log.h" @@ -47,6 +47,42 @@ char testlib_enable_tmp_setup_teardown; +TEST(mmap, zeroSize) { + ASSERT_SYS(EINVAL, MAP_FAILED, + mmap(NULL, 0, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); +} + +TEST(mmap, overflow) { + ASSERT_SYS(EINVAL, MAP_FAILED, + mmap(NULL, 0x800000000000, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0)); + ASSERT_SYS(EINVAL, MAP_FAILED, + mmap(NULL, 0x7fffffffffff, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0)); +} + +TEST(mmap, outOfAutomapRange) { + ASSERT_SYS( + ENOMEM, MAP_FAILED, + mmap(NULL, kAutomapSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); +} + +TEST(mmap, noreplaceImage) { + ASSERT_SYS(EEXIST, MAP_FAILED, + mmap(_base, FRAMESIZE, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0)); +} + +TEST(mmap, noreplaceExistingMap) { + char *p; + ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); + ASSERT_SYS(EEXIST, MAP_FAILED, + mmap(p, FRAMESIZE, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0)); + EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); +} + TEST(mmap, testMapFile) { int fd; char *p; @@ -148,6 +184,25 @@ TEST(mmap, mapPrivate_writesDontChangeFile) { EXPECT_NE(-1, close(fd)); } +TEST(mmap, twoPowerSize_automapsAddressWithThatAlignment) { + char *q, *p; + // increase the likelihood automap is unaligned w.r.t. following call + ASSERT_NE(MAP_FAILED, (q = mmap(NULL, 0x00010000, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0))); + // ask for a nice big round size + ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 0x00080000, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0))); + // verify it's aligned + ASSERT_EQ(0, (intptr_t)p & 0x0007ffff); + EXPECT_SYS(0, 0, munmap(p, 0x00080000)); + // now try again with a big size that isn't a two power + ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 0x00070000, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0))); + // automap doesn't bother aligning it + ASSERT_NE(0, (intptr_t)p & 0x0007ffff); + EXPECT_SYS(0, 0, munmap(q, 0x00010000)); +} + TEST(isheap, nullPtr) { ASSERT_FALSE(_isheap(NULL)); } diff --git a/test/libc/runtime/munmap_test.c b/test/libc/runtime/munmap_test.c index 47b230356..3cbbafa68 100644 --- a/test/libc/runtime/munmap_test.c +++ b/test/libc/runtime/munmap_test.c @@ -233,12 +233,12 @@ TEST(munmap, tinyFile_preciseUnmapSize) { // clang-format off TEST(munmap, tinyFile_mapThriceUnmapOnce) { - char *p; + char *p = (char *)0x02000000; ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644)); ASSERT_SYS (0, 5, write(3, "hello", 5)); - ASSERT_NE(MAP_FAILED, (p=mmap(0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))); - ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE, 3, 0)); - ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE, 3, 0)); + ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*0, FRAMESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0)); + ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); + ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); ASSERT_SYS(0, 0, close(3)); EXPECT_TRUE(MemoryExists(p+FRAMESIZE*0)); EXPECT_TRUE(MemoryExists(p+FRAMESIZE*1)); diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index d2cc826bb..6c009af2c 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -58,7 +58,7 @@ o/$(MODE)/third_party/dlmalloc/dlmalloc.o: \ # we must segregate codegen because: # file contains multiple independently linkable apis -o/$(MODE)/third_party/dlmalloc/dlmalloc.greg.o: \ +o/$(MODE)/third_party/dlmalloc/dlmalloc.o: \ OVERRIDE_CFLAGS += \ -ffunction-sections \ -fdata-sections diff --git a/third_party/dlmalloc/vespene.greg.c b/third_party/dlmalloc/vespene.c similarity index 100% rename from third_party/dlmalloc/vespene.greg.c rename to third_party/dlmalloc/vespene.c diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index afa16db70..33eb69ac4 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -255,7 +255,7 @@ static ssize_t pushline (lua_State *L, int firstline) { rc = b ? 1 : -1; } if (!(rc == -1 && errno == EAGAIN)) { - write(1, "\n", 1); + write(1, "\n", 1); } if (rc == -1 || (!rc && !b)) { return rc; diff --git a/tool/build/runit.c b/tool/build/runit.c index 63109d038..b11bce9c7 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -314,8 +314,8 @@ static void Send(const void *output, size_t outputsize) { static z_stream zs; static char zbuf[4096]; if (!once) { - CHECK_EQ(Z_OK, deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)); + CHECK_EQ(Z_OK, deflateInit2(&zs, 4, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY)); once = true; } zs.next_in = output; diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 8bf8de2ab..45ab5e634 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -60,6 +60,7 @@ #include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtscp.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" @@ -406,7 +407,6 @@ static bool sslcliused; static bool loglatency; static bool terminated; static bool uniprocess; -static bool memmonalive; static bool invalidated; static bool logmessages; static bool isinitialized; @@ -430,7 +430,7 @@ static bool loggednetworkorigin; static bool ishandlingconnection; static bool hasonclientconnection; static bool evadedragnetsurveillance; -static _Atomic(bool) terminatemonitor; +_Atomic(bool) static terminatemonitor; static int zfd; static int frags; @@ -440,6 +440,7 @@ static int mainpid; static int sandboxed; static int changeuid; static int changegid; +static int monitortid; static int isyielding; static int statuscode; static int shutdownsig; @@ -6391,7 +6392,7 @@ static int ExitWorker(void) { } if (monitortty) { terminatemonitor = true; - _spinlock(&memmonalive); + _spinlock(&monitortid); } _Exit(0); } @@ -6505,7 +6506,6 @@ static int MemoryMonitor(void *arg) { struct MemoryInterval *mi, *mi2; long i, j, k, n, x, y, pi, gen, pages; int rc, id, color, color2, workers; - _spinlock(&memmonalive); id = atomic_load_explicit(&shared->workers, memory_order_relaxed); DEBUGF("(memv) started for pid %d on tid %d", getpid(), gettid()); @@ -6538,8 +6538,7 @@ static int MemoryMonitor(void *arg) { _spunlock(&shared->montermlock); if (tty != -1) { - for (gen = 0, mi = 0, b = 0;;) { - if (terminatemonitor) break; + for (gen = 0, mi = 0, b = 0; !terminatemonitor;) { workers = atomic_load_explicit(&shared->workers, memory_order_relaxed); if (id) id = MAX(1, MIN(id, workers)); if (!id && workers) { @@ -6566,7 +6565,7 @@ static int MemoryMonitor(void *arg) { } _spunlock(&_mmi.lock); if (!ok) { - WARNF("(memv) retrying due to contention on mmap table"); + VERBOSEF("(memv) retrying due to contention on mmap table"); continue; } @@ -6632,18 +6631,25 @@ static int MemoryMonitor(void *arg) { free(mi); free(b); } - _spunlock(&memmonalive); + DEBUGF("(memv) done"); return 0; } -static int MonitorMemory(void) { - return clone(MemoryMonitor, - mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, - MAP_STACK | MAP_ANONYMOUS, -1, 0), - FRAMESIZE, - CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, - 0, 0, 0, 0, 0); +static void MonitorMemory(void) { + char *tls; + monitortid = -1; + tls = __initialize_tls(malloc(64)); + __cxa_atexit(free, tls, 0); + if (clone(MemoryMonitor, + mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_STACK | MAP_ANONYMOUS, -1, 0), + GetStackSize(), + CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_SETTLS | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, + 0, 0, tls, 64, &monitortid) == -1) { + monitortid = 0; + } } static int HandleConnection(size_t i) { @@ -6665,7 +6671,6 @@ static int HandleConnection(size_t i) { switch ((pid = fork())) { case 0: if (monitortty) { - memmonalive = false; MonitorMemory(); } meltdown = false; @@ -6820,8 +6825,9 @@ static int HandleReadline(void) { errno = 0; return 0; } else { - OnTerm(SIGIO); // error - return -1; + WARNF("unexpected terminal error %d% m", status); + errno = 0; + return 0; } } linenoiseDisableRawMode(); @@ -7309,7 +7315,7 @@ void RedBean(int argc, char *argv[]) { } if (monitortty) { terminatemonitor = true; - _spinlock(&memmonalive); + _spinlock(&monitortid); } INFOF("(srvr) shutdown complete"); } From c21412bc2ce38a8c0a55411b92db3e78e1fe3c23 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 19 May 2022 17:46:14 -0700 Subject: [PATCH 19/40] Remove carriage return from function traces --- libc/runtime/ftracer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index feb47416f..a7873e57e 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -120,7 +120,7 @@ privileged void ftracer(void) { frame = frame->next; if (frame->addr != g_ftrace.lastaddr) { stackuse = (intptr_t)GetStackAddr(0) + GetStackSize() - (intptr_t)frame; - kprintf("%rFUN %5P %'13T %'*ld %*s%t\r\n", g_ftrace.stackdigs, stackuse, + kprintf("%rFUN %5P %'13T %'*ld %*s%t\n", g_ftrace.stackdigs, stackuse, GetNestingLevel(frame) * 2, "", frame->addr); g_ftrace.laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); g_ftrace.lastaddr = frame->addr; @@ -137,7 +137,7 @@ textstartup int ftrace_install(void) { g_ftrace.skew = GetNestingLevelImpl(__builtin_frame_address(0)); return __hook(ftrace_hook, GetSymbolTable()); } else { - kprintf("error: --ftrace failed to open symbol table\r\n"); + kprintf("error: --ftrace failed to open symbol table\n"); return -1; } } From b4ae385a6ad2e25acc84e671907a9b4fe351242e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 19 May 2022 19:08:35 -0700 Subject: [PATCH 20/40] Fix amalgamation warnings --- libc/bits/atomic-clang.h | 36 ------------- libc/bits/atomic-gcc.h | 58 --------------------- libc/bits/atomic-gcc47.h | 34 ------------- libc/bits/atomic.h | 107 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 104 insertions(+), 131 deletions(-) delete mode 100644 libc/bits/atomic-clang.h delete mode 100644 libc/bits/atomic-gcc.h delete mode 100644 libc/bits/atomic-gcc47.h diff --git a/libc/bits/atomic-clang.h b/libc/bits/atomic-clang.h deleted file mode 100644 index 6debe5e0d..000000000 --- a/libc/bits/atomic-clang.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ -#define COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § atomics » clang ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define atomic_init(obj, value) __c11_atomic_init(obj, value) -#define atomic_thread_fence(order) __c11_atomic_thread_fence(order) -#define atomic_signal_fence(order) __c11_atomic_signal_fence(order) -#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ - success, failure) \ - __c11_atomic_compare_exchange_strong(object, expected, desired, success, \ - failure) -#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ - success, failure) \ - __c11_atomic_compare_exchange_weak(object, expected, desired, success, \ - failure) -#define atomic_exchange_explicit(object, desired, order) \ - __c11_atomic_exchange(object, desired, order) -#define atomic_fetch_add_explicit(object, operand, order) \ - __c11_atomic_fetch_add(object, operand, order) -#define atomic_fetch_and_explicit(object, operand, order) \ - __c11_atomic_fetch_and(object, operand, order) -#define atomic_fetch_or_explicit(object, operand, order) \ - __c11_atomic_fetch_or(object, operand, order) -#define atomic_fetch_sub_explicit(object, operand, order) \ - __c11_atomic_fetch_sub(object, operand, order) -#define atomic_fetch_xor_explicit(object, operand, order) \ - __c11_atomic_fetch_xor(object, operand, order) -#define atomic_load_explicit(object, order) __c11_atomic_load(object, order) -#define atomic_store_explicit(object, desired, order) \ - __c11_atomic_store(object, desired, order) - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_CLANG_H_ */ diff --git a/libc/bits/atomic-gcc.h b/libc/bits/atomic-gcc.h deleted file mode 100644 index afc0c7458..000000000 --- a/libc/bits/atomic-gcc.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ -#define COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § atomics » old gnu ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define atomic_init(obj, value) ((void)(*(obj) = (value))) -#define atomic_thread_fence(order) __sync_synchronize() -#define atomic_signal_fence(order) __asm__ volatile("" ::: "memory") -#define __atomic_apply_stride(object, operand) \ - (((__typeof__(__atomic_val(object)))0) + (operand)) -#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ - success, failure) \ - __extension__({ \ - __typeof__(expected) __ep = (expected); \ - __typeof__(*__ep) __e = *__ep; \ - (void)(success); \ - (void)(failure); \ - (_Bool)((*__ep = __sync_val_compare_and_swap(object, __e, desired)) == \ - __e); \ - }) -#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ - success, failure) \ - atomic_compare_exchange_strong_explicit(object, expected, desired, success, \ - failure) -#if __has_builtin(__sync_swap) -#define atomic_exchange_explicit(object, desired, order) \ - ((void)(order), __sync_swap(object, desired)) -#else -#define atomic_exchange_explicit(object, desired, order) \ - __extension__({ \ - __typeof__(object) __o = (object); \ - __typeof__(desired) __d = (desired); \ - (void)(order); \ - __sync_synchronize(); \ - __sync_lock_test_and_set(&__atomic_val(__o), __d); \ - }) -#endif -#define atomic_fetch_add_explicit(object, operand, order) \ - ((void)(order), \ - __sync_fetch_and_add(object, __atomic_apply_stride(object, operand))) -#define atomic_fetch_and_explicit(object, operand, order) \ - ((void)(order), __sync_fetch_and_and(object, operand)) -#define atomic_fetch_or_explicit(object, operand, order) \ - ((void)(order), __sync_fetch_and_or(object, operand)) -#define atomic_fetch_sub_explicit(object, operand, order) \ - ((void)(order), \ - __sync_fetch_and_sub(object, __atomic_apply_stride(object, operand))) -#define atomic_fetch_xor_explicit(object, operand, order) \ - ((void)(order), __sync_fetch_and_xor(object, operand)) -#define atomic_load_explicit(object, order) \ - ((void)(order), __sync_fetch_and_add(object, 0)) -#define atomic_store_explicit(object, desired, order) \ - ((void)atomic_exchange_explicit(object, desired, order)) - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC_H_ */ diff --git a/libc/bits/atomic-gcc47.h b/libc/bits/atomic-gcc47.h deleted file mode 100644 index 47928ec75..000000000 --- a/libc/bits/atomic-gcc47.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ -#define COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § atomics » gcc 4.7+ ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define atomic_init(obj, value) ((void)(*(obj) = (value))) -#define atomic_thread_fence(order) __atomic_thread_fence(order) -#define atomic_signal_fence(order) __atomic_signal_fence(order) -#define atomic_compare_exchange_strong_explicit(pObject, pExpected, desired, \ - success, failure) \ - __atomic_compare_exchange_n(pObject, pExpected, desired, 0, success, failure) -#define atomic_compare_exchange_weak_explicit(pObject, pExpected, desired, \ - success, failure) \ - __atomic_compare_exchange_n(pObject, pExpected, desired, 1, success, failure) -#define atomic_exchange_explicit(pObject, desired, order) \ - __atomic_exchange_n(pObject, desired, order) -#define atomic_fetch_add_explicit(pObject, operand, order) \ - __atomic_fetch_add(pObject, operand, order) -#define atomic_fetch_and_explicit(pObject, operand, order) \ - __atomic_fetch_and(pObject, operand, order) -#define atomic_fetch_or_explicit(pObject, operand, order) \ - __atomic_fetch_or(pObject, operand, order) -#define atomic_fetch_sub_explicit(pObject, operand, order) \ - __atomic_fetch_sub(pObject, operand, order) -#define atomic_fetch_xor_explicit(pObject, operand, order) \ - __atomic_fetch_xor(pObject, operand, order) -#define atomic_load_explicit(pObject, order) __atomic_load_n(pObject, order) -#define atomic_store_explicit(pObject, desired, order) \ - __atomic_store_n(pObject, desired, order) - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_BITS_ATOMIC_GCC47_H_ */ diff --git a/libc/bits/atomic.h b/libc/bits/atomic.h index d2073d886..9d9b27ce0 100644 --- a/libc/bits/atomic.h +++ b/libc/bits/atomic.h @@ -58,11 +58,112 @@ #define atomic_flag_clear(x) atomic_flag_clear_explicit(x, memory_order_seq_cst) #if defined(__CLANG_ATOMIC_BOOL_LOCK_FREE) -#include "libc/bits/atomic-clang.h" + +#define atomic_init(obj, value) __c11_atomic_init(obj, value) +#define atomic_thread_fence(order) __c11_atomic_thread_fence(order) +#define atomic_signal_fence(order) __c11_atomic_signal_fence(order) +#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ + success, failure) \ + __c11_atomic_compare_exchange_strong(object, expected, desired, success, \ + failure) +#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ + success, failure) \ + __c11_atomic_compare_exchange_weak(object, expected, desired, success, \ + failure) +#define atomic_exchange_explicit(object, desired, order) \ + __c11_atomic_exchange(object, desired, order) +#define atomic_fetch_add_explicit(object, operand, order) \ + __c11_atomic_fetch_add(object, operand, order) +#define atomic_fetch_and_explicit(object, operand, order) \ + __c11_atomic_fetch_and(object, operand, order) +#define atomic_fetch_or_explicit(object, operand, order) \ + __c11_atomic_fetch_or(object, operand, order) +#define atomic_fetch_sub_explicit(object, operand, order) \ + __c11_atomic_fetch_sub(object, operand, order) +#define atomic_fetch_xor_explicit(object, operand, order) \ + __c11_atomic_fetch_xor(object, operand, order) +#define atomic_load_explicit(object, order) __c11_atomic_load(object, order) +#define atomic_store_explicit(object, desired, order) \ + __c11_atomic_store(object, desired, order) + #elif (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 -#include "libc/bits/atomic-gcc47.h" + +#define atomic_init(obj, value) ((void)(*(obj) = (value))) +#define atomic_thread_fence(order) __atomic_thread_fence(order) +#define atomic_signal_fence(order) __atomic_signal_fence(order) +#define atomic_compare_exchange_strong_explicit(pObject, pExpected, desired, \ + success, failure) \ + __atomic_compare_exchange_n(pObject, pExpected, desired, 0, success, failure) +#define atomic_compare_exchange_weak_explicit(pObject, pExpected, desired, \ + success, failure) \ + __atomic_compare_exchange_n(pObject, pExpected, desired, 1, success, failure) +#define atomic_exchange_explicit(pObject, desired, order) \ + __atomic_exchange_n(pObject, desired, order) +#define atomic_fetch_add_explicit(pObject, operand, order) \ + __atomic_fetch_add(pObject, operand, order) +#define atomic_fetch_and_explicit(pObject, operand, order) \ + __atomic_fetch_and(pObject, operand, order) +#define atomic_fetch_or_explicit(pObject, operand, order) \ + __atomic_fetch_or(pObject, operand, order) +#define atomic_fetch_sub_explicit(pObject, operand, order) \ + __atomic_fetch_sub(pObject, operand, order) +#define atomic_fetch_xor_explicit(pObject, operand, order) \ + __atomic_fetch_xor(pObject, operand, order) +#define atomic_load_explicit(pObject, order) __atomic_load_n(pObject, order) +#define atomic_store_explicit(pObject, desired, order) \ + __atomic_store_n(pObject, desired, order) + #else -#include "libc/bits/atomic-gcc.h" + +#define atomic_init(obj, value) ((void)(*(obj) = (value))) +#define atomic_thread_fence(order) __sync_synchronize() +#define atomic_signal_fence(order) __asm__ volatile("" ::: "memory") +#define __atomic_apply_stride(object, operand) \ + (((__typeof__(__atomic_val(object)))0) + (operand)) +#define atomic_compare_exchange_strong_explicit(object, expected, desired, \ + success, failure) \ + __extension__({ \ + __typeof__(expected) __ep = (expected); \ + __typeof__(*__ep) __e = *__ep; \ + (void)(success); \ + (void)(failure); \ + (_Bool)((*__ep = __sync_val_compare_and_swap(object, __e, desired)) == \ + __e); \ + }) +#define atomic_compare_exchange_weak_explicit(object, expected, desired, \ + success, failure) \ + atomic_compare_exchange_strong_explicit(object, expected, desired, success, \ + failure) +#if __has_builtin(__sync_swap) +#define atomic_exchange_explicit(object, desired, order) \ + ((void)(order), __sync_swap(object, desired)) +#else +#define atomic_exchange_explicit(object, desired, order) \ + __extension__({ \ + __typeof__(object) __o = (object); \ + __typeof__(desired) __d = (desired); \ + (void)(order); \ + __sync_synchronize(); \ + __sync_lock_test_and_set(&__atomic_val(__o), __d); \ + }) +#endif +#define atomic_fetch_add_explicit(object, operand, order) \ + ((void)(order), \ + __sync_fetch_and_add(object, __atomic_apply_stride(object, operand))) +#define atomic_fetch_and_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_and(object, operand)) +#define atomic_fetch_or_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_or(object, operand)) +#define atomic_fetch_sub_explicit(object, operand, order) \ + ((void)(order), \ + __sync_fetch_and_sub(object, __atomic_apply_stride(object, operand))) +#define atomic_fetch_xor_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_xor(object, operand)) +#define atomic_load_explicit(object, order) \ + ((void)(order), __sync_fetch_and_add(object, 0)) +#define atomic_store_explicit(object, desired, order) \ + ((void)atomic_exchange_explicit(object, desired, order)) + #endif #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ From 4245da19e26c74c0393975b898a73a00adb698ad Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 04:01:50 -0700 Subject: [PATCH 21/40] Remove some old dead code from ftrace --- libc/runtime/ftracer.c | 19 ------------------- tool/net/redbean.c | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index a7873e57e..699409ad5 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -16,28 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/fmt/itoa.h" -#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchgp.h" -#include "libc/intrin/spinlock.h" -#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" -#include "libc/math.h" -#include "libc/nexgen32e/rdtsc.h" -#include "libc/nexgen32e/rdtscp.h" #include "libc/nexgen32e/stackframe.h" -#include "libc/nexgen32e/x86feature.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" -#include "libc/stdio/stdio.h" -#include "libc/sysv/consts/map.h" -#include "libc/sysv/consts/o.h" -#include "libc/time/clockstonanos.internal.h" #define MAX_NESTING 512 @@ -57,7 +43,6 @@ static struct Ftrace { int skew; int stackdigs; int64_t lastaddr; - uint64_t laststamp; } g_ftrace; static privileged int GetNestingLevelImpl(struct StackFrame *frame) { @@ -112,17 +97,14 @@ static privileged inline bool AcquireFtraceLock(void) { */ privileged void ftracer(void) { long stackuse; - uint64_t stamp; struct StackFrame *frame; if (AcquireFtraceLock()) { - stamp = rdtsc(); frame = __builtin_frame_address(0); frame = frame->next; if (frame->addr != g_ftrace.lastaddr) { stackuse = (intptr_t)GetStackAddr(0) + GetStackSize() - (intptr_t)frame; kprintf("%rFUN %5P %'13T %'*ld %*s%t\n", g_ftrace.stackdigs, stackuse, GetNestingLevel(frame) * 2, "", frame->addr); - g_ftrace.laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); g_ftrace.lastaddr = frame->addr; } ReleaseFtraceLock(); @@ -132,7 +114,6 @@ privileged void ftracer(void) { textstartup int ftrace_install(void) { if (GetSymbolTable()) { g_ftrace.lastaddr = -1; - g_ftrace.laststamp = kStartTsc; g_ftrace.stackdigs = LengthInt64Thousands(GetStackSize()); g_ftrace.skew = GetNestingLevelImpl(__builtin_frame_address(0)); return __hook(ftrace_hook, GetSymbolTable()); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 45ab5e634..ee5f1a3a0 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -6552,10 +6552,13 @@ static int MemoryMonitor(void *arg) { mi = mi2; mi[0].x = (intptr_t)_base >> 16; mi[0].size = _etext - _base; + mi[0].flags = 0; mi[1].x = (intptr_t)_etext >> 16; mi[1].size = _edata - _etext; + mi[1].flags = 0; mi[2].x = (intptr_t)_edata >> 16; mi[2].size = _end - _edata; + mi[2].flags = 0; _spinlock(&_mmi.lock); if (_mmi.i == intervals - 3) { memcpy(mi + 3, _mmi.p, _mmi.i * sizeof(*mi)); @@ -6585,7 +6588,11 @@ static int MemoryMonitor(void *arg) { rc = mincore(addr + j * PAGESIZE, PAGESIZE, &rez); if (!rc) { if (rez & 1) { - color2 = 42; + if (mi[i].flags & MAP_SHARED) { + color2 = 105; + } else { + color2 = 42; + } } else { color2 = 41; } @@ -6597,7 +6604,11 @@ static int MemoryMonitor(void *arg) { color = color2; appendf(&b, "\e[%dm", color); } - appendw(&b, ' '); + if (mi[i].flags & MAP_ANONYMOUS) { + appendw(&b, ' '); + } else { + appendw(&b, '/'); + } } } @@ -7294,6 +7305,7 @@ void RedBean(int argc, char *argv[]) { #else GetHostsTxt(); // for effect GetResolvConf(); // for effect + __print_maps(); if (daemonize || uniprocess || !linenoiseIsTerminal()) { EventLoop(HEARTBEAT); } else if (IsWindows()) { From c8a2f040584691fb38062cf0d58b729e7881288c Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 04:46:42 -0700 Subject: [PATCH 22/40] Reduce ftrace overhead to 280ns --- libc/intrin/kprintf.greg.c | 10 ++++++++-- libc/runtime/ftracer.c | 40 ++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 034075d47..f3340e947 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ShouldUseMsabiAttribute() 1 +#include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" @@ -707,9 +708,14 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, if (p < e) *p = hash; ++p; } - for (; cols > i; --cols) { - if (p < e) { + while (cols > i) { + if (p + 8 < e && cols - i > 8) { + WRITE64LE(p, 0x2020202020202020); + cols -= 8; + p += 8; + } else if (p < e) { *p++ = ' '; + --cols; } else { p = kadvance(p, e, cols - i); break; diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 699409ad5..58deed8bb 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -18,10 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchgp.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" @@ -69,21 +71,29 @@ static privileged inline void ReleaseFtraceLock(void) { static privileged inline bool AcquireFtraceLock(void) { int me, owner, tries; - for (tries = 0, me = gettid();;) { - owner = 0; - if (_lockcmpxchgp(&ftrace_lock, &owner, me)) { - return true; - } - if (owner == me) { - // we ignore re-entry into ftrace. while the code and build config - // is written to make re-entry highly unlikely, it's impossible to - // guarantee. there's also the possibility of asynchronous signals - return false; - } - if (++tries & 7) { - __builtin_ia32_pause(); - } else { - sched_yield(); + if (!__threaded) { + return _cmpxchg(&ftrace_lock, 0, -1); + } else { + for (tries = 0, me = gettid();;) { + owner = 0; + if (_lockcmpxchgp(&ftrace_lock, &owner, me)) { + return true; + } + if (owner == -1) { + // avoid things getting weird after first clone() call transition + return false; + } + if (owner == me) { + // we ignore re-entry into ftrace. while the code and build config + // is written to make re-entry highly unlikely, it's impossible to + // guarantee. there's also the possibility of asynchronous signals + return false; + } + if (++tries & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } } } } From fb4382e9a45fc745f499fc4ab45211df2eaf7cd5 Mon Sep 17 00:00:00 2001 From: Gautham <41098605+ahgamut@users.noreply.github.com> Date: Fri, 20 May 2022 20:47:10 +0530 Subject: [PATCH 23/40] os.realname variable for correct USER_SITE value (#410) In site.py, Python uses os.name to decide where the USER_SITE (ie the folder containing the user's locally installed packages) is located. With cosmo we have set os.name as "posix" always, so we use a new os.realname to decide the USER_SITE location. --- third_party/python/Lib/os.py | 1 + third_party/python/Lib/site.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/third_party/python/Lib/os.py b/third_party/python/Lib/os.py index 76f62113b..df52811da 100644 --- a/third_party/python/Lib/os.py +++ b/third_party/python/Lib/os.py @@ -42,6 +42,7 @@ def _get_exports_list(module): name = 'posix' linesep = '\n' +realname = "nt" if cosmo.kernel == "nt" else "posix" from posix import * from posix import _exit __all__.append('_exit') diff --git a/third_party/python/Lib/site.py b/third_party/python/Lib/site.py index 40b0c865a..a58ae18da 100644 --- a/third_party/python/Lib/site.py +++ b/third_party/python/Lib/site.py @@ -240,7 +240,7 @@ def _getuserbase(): def joinuser(*args): return os.path.expanduser(os.path.join(*args)) - if os.name == "nt": + if os.realname == "nt": base = os.environ.get("APPDATA") or "~" if env_base: return env_base @@ -298,7 +298,7 @@ def getusersitepackages(): "posix_user":'{userbase}/lib/python3.6/site-packages', "nt_user": "{userbase}/Python36/site-packages", } - USER_SITE = purelib_map.get('%s_user' % os.name).format(userbase=user_base) + USER_SITE = purelib_map.get('%s_user' % os.realname).format(userbase=user_base) return USER_SITE def addusersitepackages(known_paths): From 8141988e92d8e29bd70ccfd0aa7b62708a9fe486 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 07:37:51 -0700 Subject: [PATCH 24/40] Shave 4kb off default mode binaries --- libc/calls/calls.mk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 1845696bf..6e359fdbe 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -134,6 +134,12 @@ o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: \ -ffunction-sections \ -fdata-sections +# we want small code size because: +# to keep .text.head under 4096 bytes +o/$(MODE)/libc/calls/mman.greg.o: \ + OVERRIDE_COPTS += \ + -Os + # we always want -Os because: # va_arg codegen is very bloated in default mode o//libc/calls/open.o \ From f4fc16754ac9bae1ed12117bdd00578798729d1e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 14:20:54 -0700 Subject: [PATCH 25/40] Fix build invalidation issue with GNU Make The `HDRS` and/or `INCS` variables need to be defined in order for `mkdeps.com` to compute build dependencies for `o//depend`. We're using INCS here since makeint.h is the evil kind of header that isn't actually defining a library interface but rather is a hodgepodge of common code. --- third_party/make/ar.c | 2 +- third_party/make/arscan.c | 2 +- third_party/make/commands.c | 2 +- third_party/make/default.c | 2 +- third_party/make/dir.c | 2 +- third_party/make/expand.c | 2 +- third_party/make/file.c | 2 +- third_party/make/function.c | 2 +- third_party/make/guile.c | 2 +- third_party/make/hash.c | 2 +- third_party/make/implicit.c | 2 +- third_party/make/job.c | 2 +- third_party/make/load.c | 2 +- third_party/make/loadapi.c | 2 +- third_party/make/main.c | 2 +- third_party/make/make.mk | 3 +++ third_party/make/{makeint.h => makeint.inc} | 0 third_party/make/misc.c | 2 +- third_party/make/output.c | 2 +- third_party/make/posixos.c | 2 +- third_party/make/read.c | 2 +- third_party/make/remake.c | 2 +- third_party/make/remote-stub.c | 2 +- third_party/make/rule.c | 2 +- third_party/make/signame.c | 2 +- third_party/make/strcache.c | 2 +- third_party/make/variable.c | 2 +- third_party/make/vpath.c | 2 +- 28 files changed, 29 insertions(+), 26 deletions(-) rename third_party/make/{makeint.h => makeint.inc} (100%) diff --git a/third_party/make/ar.c b/third_party/make/ar.c index 2b4cd30a8..f55738e6e 100644 --- a/third_party/make/ar.c +++ b/third_party/make/ar.c @@ -14,7 +14,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "libc/alg/alg.h" #include "third_party/make/dep.h" diff --git a/third_party/make/arscan.c b/third_party/make/arscan.c index ce942ec6c..925e12783 100644 --- a/third_party/make/arscan.c +++ b/third_party/make/arscan.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "libc/sysv/consts/o.h" -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /* clang-format off */ #ifdef TEST diff --git a/third_party/make/commands.c b/third_party/make/commands.c index 0792db7f3..48f98daed 100644 --- a/third_party/make/commands.c +++ b/third_party/make/commands.c @@ -14,7 +14,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/dep.h" #include "third_party/make/filedef.h" diff --git a/third_party/make/default.c b/third_party/make/default.c index b71edf8f5..1ba88c285 100644 --- a/third_party/make/default.c +++ b/third_party/make/default.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/filedef.h" #include "third_party/make/variable.h" diff --git a/third_party/make/dir.c b/third_party/make/dir.c index fdfdd6686..b8b884ba9 100644 --- a/third_party/make/dir.c +++ b/third_party/make/dir.c @@ -14,7 +14,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/dep.h" #include "third_party/make/filedef.h" diff --git a/third_party/make/expand.c b/third_party/make/expand.c index bb09d877c..7ab3674f9 100644 --- a/third_party/make/expand.c +++ b/third_party/make/expand.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/filedef.h" #include "third_party/make/job.h" diff --git a/third_party/make/file.c b/third_party/make/file.c index a2c87f5dc..64c19216d 100644 --- a/third_party/make/file.c +++ b/third_party/make/file.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/filedef.h" #include "third_party/make/dep.h" diff --git a/third_party/make/function.c b/third_party/make/function.c index 4e70429f5..35adaeb4f 100644 --- a/third_party/make/function.c +++ b/third_party/make/function.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/filedef.h" #include "third_party/make/variable.h" diff --git a/third_party/make/guile.c b/third_party/make/guile.c index 2f9d3a2c7..b3a5e7099 100644 --- a/third_party/make/guile.c +++ b/third_party/make/guile.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" int guile_gmake_setup (const floc *flocp UNUSED) diff --git a/third_party/make/hash.c b/third_party/make/hash.c index 6ac3aa6e4..7f4806b01 100644 --- a/third_party/make/hash.c +++ b/third_party/make/hash.c @@ -14,7 +14,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "libc/assert.h" #include "third_party/make/hash.h" diff --git a/third_party/make/implicit.c b/third_party/make/implicit.c index 7e46e9022..e83e5192c 100644 --- a/third_party/make/implicit.c +++ b/third_party/make/implicit.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/rule.h" #include "third_party/make/dep.h" diff --git a/third_party/make/job.c b/third_party/make/job.c index df958aa3e..907a1f693 100644 --- a/third_party/make/job.c +++ b/third_party/make/job.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "third_party/make/debug.h" #include "third_party/make/filedef.h" diff --git a/third_party/make/load.c b/third_party/make/load.c index 9a5c77a1e..86466a8fc 100644 --- a/third_party/make/load.c +++ b/third_party/make/load.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" int load_file (const floc *flocp, const char **ldname UNUSED, int noerror) diff --git a/third_party/make/loadapi.c b/third_party/make/loadapi.c index 035ef3682..627e270b6 100644 --- a/third_party/make/loadapi.c +++ b/third_party/make/loadapi.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/variable.h" diff --git a/third_party/make/main.c b/third_party/make/main.c index 2fa0845b2..ec5ffea02 100644 --- a/third_party/make/main.c +++ b/third_party/make/main.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/os.h" #include "third_party/make/filedef.h" #include "third_party/make/dep.h" diff --git a/third_party/make/make.mk b/third_party/make/make.mk index fd472595e..8b2da2777 100644 --- a/third_party/make/make.mk +++ b/third_party/make/make.mk @@ -13,6 +13,9 @@ THIRD_PARTY_MAKE_BINS = \ THIRD_PARTY_MAKE_A = \ o/$(MODE)/third_party/make/make.a +THIRD_PARTY_MAKE_INCS = \ + o/$(MODE)/third_party/make/makeint.inc + THIRD_PARTY_MAKE_CHECKS = \ $(THIRD_PARTY_MAKE_A).pkg diff --git a/third_party/make/makeint.h b/third_party/make/makeint.inc similarity index 100% rename from third_party/make/makeint.h rename to third_party/make/makeint.inc diff --git a/third_party/make/misc.c b/third_party/make/misc.c index 82dbd4b9e..ee93ae300 100644 --- a/third_party/make/misc.c +++ b/third_party/make/misc.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/dep.h" #include "third_party/make/debug.h" diff --git a/third_party/make/output.c b/third_party/make/output.c index 8d576a11b..ecfc4b1e9 100644 --- a/third_party/make/output.c +++ b/third_party/make/output.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/os.h" #include "third_party/make/output.h" #include "libc/calls/struct/flock.h" diff --git a/third_party/make/posixos.c b/third_party/make/posixos.c index 084b3182a..058fbfb19 100644 --- a/third_party/make/posixos.c +++ b/third_party/make/posixos.c @@ -14,7 +14,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /**/ #include "libc/sysv/consts/sa.h" #include "third_party/make/debug.h" diff --git a/third_party/make/read.c b/third_party/make/read.c index ff81d8c61..696d60a97 100644 --- a/third_party/make/read.c +++ b/third_party/make/read.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/dep.h" #include "third_party/make/job.h" diff --git a/third_party/make/remake.c b/third_party/make/remake.c index 53ed0702f..2c50d2b81 100644 --- a/third_party/make/remake.c +++ b/third_party/make/remake.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/job.h" #include "third_party/make/commands.h" diff --git a/third_party/make/remote-stub.c b/third_party/make/remote-stub.c index 16c8d340d..4f51daae1 100644 --- a/third_party/make/remote-stub.c +++ b/third_party/make/remote-stub.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/job.h" #include "third_party/make/commands.h" diff --git a/third_party/make/rule.c b/third_party/make/rule.c index 03c833fc8..9d1503275 100644 --- a/third_party/make/rule.c +++ b/third_party/make/rule.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/dep.h" #include "third_party/make/job.h" diff --git a/third_party/make/signame.c b/third_party/make/signame.c index d390ab0cd..461f2e5f4 100644 --- a/third_party/make/signame.c +++ b/third_party/make/signame.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" /* If the system provides strsignal, we don't need it. */ diff --git a/third_party/make/strcache.c b/third_party/make/strcache.c index 4c515c8bb..70a9e2f15 100644 --- a/third_party/make/strcache.c +++ b/third_party/make/strcache.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/hash.h" /* A string cached here will never be freed, so we don't need to worry about diff --git a/third_party/make/variable.c b/third_party/make/variable.c index 048f92279..8daba9bd6 100644 --- a/third_party/make/variable.c +++ b/third_party/make/variable.c @@ -15,7 +15,7 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/dep.h" diff --git a/third_party/make/vpath.c b/third_party/make/vpath.c index 545e10ae5..643701939 100644 --- a/third_party/make/vpath.c +++ b/third_party/make/vpath.c @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* clang-format off */ -#include "third_party/make/makeint.h" +#include "third_party/make/makeint.inc" #include "third_party/make/filedef.h" #include "third_party/make/variable.h" From 96781d067923e2b6a030bc9668adb6c5982e6bb5 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 15:08:02 -0700 Subject: [PATCH 26/40] Fix mistake with previous commit --- third_party/make/make.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/make/make.mk b/third_party/make/make.mk index 8b2da2777..8b703b661 100644 --- a/third_party/make/make.mk +++ b/third_party/make/make.mk @@ -14,7 +14,7 @@ THIRD_PARTY_MAKE_A = \ o/$(MODE)/third_party/make/make.a THIRD_PARTY_MAKE_INCS = \ - o/$(MODE)/third_party/make/makeint.inc + third_party/make/makeint.inc THIRD_PARTY_MAKE_CHECKS = \ $(THIRD_PARTY_MAKE_A).pkg From 7838edae880004e43c60dec68d7225046533c73d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 20 May 2022 18:51:41 -0700 Subject: [PATCH 27/40] Fix many thread and file descriptor issues on Windows The greenbean web server now works nearly perfectly on Windows with over 1000 threads. But some synchronization issues still remain which prevent us from going over nine thousand. --- examples/greenbean.c | 5 +- libc/calls/dup-nt.c | 39 ++++++++----- libc/calls/internal.h | 2 +- libc/calls/interrupts-nt.c | 25 +++++++- libc/calls/ioctl_tiocgwinsz-nt.c | 14 +++-- libc/calls/pipe-nt.c | 8 ++- libc/calls/reservefd.c | 58 +++++++++++-------- libc/calls/sigchld-nt.c | 10 +++- libc/intrin/_spinlock_debug_4.c | 14 +++-- ..._spinlock_debug_1.c => _trylock_debug_4.c} | 50 ++++++---------- libc/intrin/gettid.greg.c | 8 ++- libc/intrin/intrin.mk | 15 ++++- libc/intrin/once.h | 28 ++++----- libc/intrin/releasefd.c | 7 --- libc/intrin/releasefd_unlocked.c | 27 +++++++++ libc/intrin/sched_yield.S | 1 + libc/intrin/spinlock.h | 54 ++++++++--------- libc/intrin/virtualprotect.c | 15 ++--- libc/nexgen32e/threaded.h | 4 +- libc/rand/rand64.c | 2 +- libc/runtime/cosmo.S | 10 ++++ libc/runtime/getsymboltable.c | 2 +- libc/runtime/hook.greg.c | 1 + libc/runtime/symbols.c | 30 ++++++++++ libc/sock/accept-nt.c | 10 +++- libc/sock/epoll.c | 6 +- libc/sock/socket-nt.c | 3 +- libc/sock/socketpair-nt.c | 55 ++++++++++-------- libc/zipos/get.c | 2 +- libc/zipos/open.c | 4 ++ test/libc/calls/open_test.c | 25 ++++++++ test/libc/runtime/clone_test.c | 21 +++---- 32 files changed, 363 insertions(+), 192 deletions(-) rename libc/intrin/{_spinlock_debug_1.c => _trylock_debug_4.c} (67%) create mode 100644 libc/intrin/releasefd_unlocked.c create mode 100644 libc/runtime/symbols.c diff --git a/examples/greenbean.c b/examples/greenbean.c index 45661cd15..3698fcf45 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -100,7 +100,7 @@ int workers; int messages; int connections; const char *status; -volatile int closingtime; +_Atomic(int) closingtime; int Worker(void *id) { int server, yes = 1; @@ -273,8 +273,7 @@ int main(int argc, char *argv[]) { MAP_STACK | MAP_ANONYMOUS, -1, 0); CHECK_NE(-1, clone(Worker, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_SETTID | - CLONE_CHILD_CLEARTID, + CLONE_SIGHAND | CLONE_SETTLS, (void *)(intptr_t)i, 0, tls, 64, 0)); } status = ""; diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 7fcfff765..e819fb3a6 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -48,26 +48,33 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } // allocate a new file descriptor - if (newfd == -1) { - if ((newfd = __reservefd_unlocked(start)) == -1) { - _spunlock(&__fds_lock); - return -1; + for (;;) { + if (newfd == -1) { + if ((newfd = __reservefd_unlocked(start)) == -1) { + _spunlock(&__fds_lock); + return -1; + } + break; + } else { + if (__ensurefds_unlocked(newfd) == -1) { + _spunlock(&__fds_lock); + return -1; + } + if (g_fds.p[newfd].kind) { + _spunlock(&__fds_lock); + close(newfd); + _spinlock(&__fds_lock); + } + if (!g_fds.p[newfd].kind) { + g_fds.p[newfd].kind = kFdReserved; + break; + } } - } else { - if (__ensurefds_unlocked(newfd) == -1) { - _spunlock(&__fds_lock); - return -1; - } - if (g_fds.p[newfd].kind) { - _spunlock(&__fds_lock); - close(newfd); - _spinlock(&__fds_lock); - } - g_fds.p[newfd].kind = kFdReserved; } handle = g_fds.p[oldfd].handle; proc = GetCurrentProcess(); + if (DuplicateHandle(proc, handle, proc, &g_fds.p[newfd].handle, 0, true, kNtDuplicateSameAccess)) { g_fds.p[newfd].kind = g_fds.p[oldfd].kind; @@ -82,7 +89,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } rc = newfd; } else { - __releasefd(newfd); + __releasefd_unlocked(newfd); rc = __winerr(); } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 9d7ab37ea..44e6fa79a 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -66,7 +66,7 @@ struct Fd { }; struct Fds { - size_t f; /* lowest free slot */ + int f; /* lowest free slot */ size_t n; /* monotonic capacity */ struct Fd *p; struct Fd __init_p[OPEN_MAX]; diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index b64e78198..eda802dab 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -23,16 +23,35 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" -#include "libc/intrin/spinlock.h" +#include "libc/intrin/lockcmpxchgp.h" +#include "libc/nexgen32e/threaded.h" + +_Alignas(64) static int rlock; + +static privileged inline bool AcquireInterruptPollLock(void) { + // any thread can poll for interrupts + // but it's wasteful to have every single thread doing it + int me, owner, tries; + if (!__threaded) return true; + me = gettid(); + owner = 0; + if (_lockcmpxchgp(&rlock, &owner, me)) return true; + return owner == me; +} + +static textwindows inline void ReleaseInterruptPollLock(void) { + int zero = 0; + __atomic_store(&rlock, &zero, __ATOMIC_RELAXED); +} textwindows bool _check_interrupts(bool restartable, struct Fd *fd) { bool res; if (__time_critical) return false; - if (_trylock(&__fds_lock)) return false; + if (!AcquireInterruptPollLock()) return false; if (weaken(_check_sigalrm)) weaken(_check_sigalrm)(); if (weaken(_check_sigchld)) weaken(_check_sigchld)(); if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd); res = weaken(__sig_check) && weaken(__sig_check)(restartable); - _spunlock(&__fds_lock); + ReleaseInterruptPollLock(); return res; } diff --git a/libc/calls/ioctl_tiocgwinsz-nt.c b/libc/calls/ioctl_tiocgwinsz-nt.c index 5080a3df5..53dafe89c 100644 --- a/libc/calls/ioctl_tiocgwinsz-nt.c +++ b/libc/calls/ioctl_tiocgwinsz-nt.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/winsize.h" +#include "libc/intrin/spinlock.h" #include "libc/log/log.h" #include "libc/nt/console.h" #include "libc/nt/enum/startf.h" @@ -31,13 +32,15 @@ #include "libc/sysv/errfuns.h" textwindows int ioctl_tiocgwinsz_nt(struct Fd *fd, struct winsize *ws) { - int i, e; + int i, e, rc; uint32_t mode; struct Fd *fds[3]; struct NtStartupInfo startinfo; struct NtConsoleScreenBufferInfoEx sbinfo; + rc = -1; e = errno; if (ws) { + _spinlock(&__fds_lock); fds[0] = fd, fds[1] = g_fds.p + 1, fds[2] = g_fds.p + 0; GetStartupInfo(&startinfo); for (i = 0; i < ARRAYLEN(fds); ++i) { @@ -51,14 +54,16 @@ textwindows int ioctl_tiocgwinsz_nt(struct Fd *fd, struct winsize *ws) { ws->ws_xpixel = 0; ws->ws_ypixel = 0; errno = e; - return 0; + rc = 0; + break; } else if (startinfo.dwFlags & kNtStartfUsecountchars) { ws->ws_col = startinfo.dwXCountChars; ws->ws_row = startinfo.dwYCountChars; ws->ws_xpixel = 0; ws->ws_ypixel = 0; errno = e; - return 0; + rc = 0; + break; } else { __winerr(); } @@ -69,8 +74,9 @@ textwindows int ioctl_tiocgwinsz_nt(struct Fd *fd, struct winsize *ws) { ebadf(); } } + _spunlock(&__fds_lock); } else { efault(); } - return -1; + return rc; } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index fde3fa4a8..b8d554b57 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -49,9 +49,11 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { } else { mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage; } - if ((hin = CreateNamedPipe( - pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, 1, - PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable)) != -1) { + _spunlock(&__fds_lock); + hin = CreateNamedPipe(pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, + mode, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable); + _spinlock(&__fds_lock); + if (hin != -1) { if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) { g_fds.p[reader].kind = kFdFile; diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index fd266d361..a8ad44180 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -29,33 +29,38 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +// XXX: until we can add read locks to all the code that uses g_fds.p +// (right now we only have write locks) we need to keep old copies +// of g_fds.p around after it's been extended, so that threads +// which are using an fd they de facto own can continue reading +static void FreeOldFdsArray(void *p) { + weaken(free)(p); +} + /** * Grows file descriptor array memory if needed. */ int __ensurefds_unlocked(int fd) { size_t n1, n2; struct Fd *p1, *p2; + if (fd < g_fds.n) return fd; + STRACE("__ensurefds(%d) extending", fd); + if (!weaken(malloc)) return emfile(); + p1 = g_fds.p; n1 = g_fds.n; - if (fd >= n1) { - STRACE("__ensurefds(%d) extending", fd); - if (weaken(malloc)) { - // TODO(jart): we need a semaphore for this - p1 = g_fds.p; - n2 = fd + (fd >> 1); - if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) { - memcpy(p2, p1, n1 * sizeof(*p1)); - g_fds.p = p2; - g_fds.n = n2; - if (p1 != g_fds.__init_p) { - __cxa_atexit(free, p1, 0); - } - } else { - fd = enomem(); - } - } else { - fd = emfile(); - } + if (p1 == g_fds.__init_p) { + if (!(p2 = weaken(malloc)(sizeof(g_fds.__init_p)))) return -1; + memcpy(p2, p1, sizeof(g_fds.__init_p)); + g_fds.p = p1 = p2; } + n2 = n1; + while (n2 <= fd) n2 *= 2; + if (!(p2 = weaken(malloc)(n2 * sizeof(*p1)))) return -1; + __cxa_atexit(FreeOldFdsArray, p1, 0); + memcpy(p2, p1, n1 * sizeof(*p1)); + bzero(p2 + n1, (p2 + n2) - (p2 + n1)); + g_fds.p = p2; + g_fds.n = n2; return fd; } @@ -74,7 +79,7 @@ int __ensurefds(int fd) { */ int __reservefd_unlocked(int start) { int fd; - for (fd = g_fds.f; fd < g_fds.n; ++fd) { + for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) { if (!g_fds.p[fd].kind) { break; } @@ -100,10 +105,10 @@ int __reservefd(int start) { * Closes non-stdio file descriptors to free dynamic memory. */ static void FreeFds(void) { - int i; - NTTRACE("FreeFds()"); + int i, keep = 3; + STRACE("FreeFds()"); _spinlock(&__fds_lock); - for (i = 3; i < g_fds.n; ++i) { + for (i = keep; i < g_fds.n; ++i) { if (g_fds.p[i].kind) { _spunlock(&__fds_lock); close(i); @@ -111,8 +116,11 @@ static void FreeFds(void) { } } if (g_fds.p != g_fds.__init_p) { - memcpy(g_fds.__init_p, g_fds.p, sizeof(*g_fds.p) * 3); - weaken(free)(g_fds.p); + bzero(g_fds.__init_p, sizeof(g_fds.__init_p)); + memcpy(g_fds.__init_p, g_fds.p, sizeof(*g_fds.p) * keep); + if (weaken(free)) { + weaken(free)(g_fds.p); + } g_fds.p = g_fds.__init_p; g_fds.n = ARRAYLEN(g_fds.__init_p); } diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c index 8ca410c0d..c17804392 100644 --- a/libc/calls/sigchld-nt.c +++ b/libc/calls/sigchld-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/enum/wait.h" #include "libc/nt/runtime.h" #include "libc/nt/synchronization.h" @@ -38,7 +39,10 @@ void _check_sigchld(void) { int pids[64]; uint32_t i, n; int64_t handles[64]; - if (!(n = __sample_pids(pids, handles, true))) return; + _spinlock(&__fds_lock); + n = __sample_pids(pids, handles, true); + _spunlock(&__fds_lock); + if (!n) return; i = WaitForMultipleObjects(n, handles, false, 0); if (i == kNtWaitTimeout) return; if (i == kNtWaitFailed) { @@ -53,8 +57,10 @@ void _check_sigchld(void) { if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) { STRACE("SIGCHILD SA_NOCLDWAIT fd=%d handle=%ld", pids[i], handles[i]); CloseHandle(handles[i]); - __releasefd_unlocked(pids[i]); + __releasefd(pids[i]); } + _spinlock(&__fds_lock); g_fds.p[pids[i]].zombie = true; + _spunlock(&__fds_lock); __sig_add(SIGCHLD, CLD_EXITED); } diff --git a/libc/intrin/_spinlock_debug_4.c b/libc/intrin/_spinlock_debug_4.c index e42dfbbcf..40416c2af 100644 --- a/libc/intrin/_spinlock_debug_4.c +++ b/libc/intrin/_spinlock_debug_4.c @@ -16,24 +16,30 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchgp.h" #include "libc/intrin/spinlock.h" +#include "libc/log/log.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/runtime/internal.h" #include "libc/time/clockstonanos.internal.h" -void _spinlock_debug_4(void *lockptr, const char *lockname, const char *file, - int line, const char *func) { +privileged void _spinlock_debug_4(int *lock, const char *lockname, + const char *file, int line, + const char *func) { unsigned i; + int me, owner; uint64_t ts1, ts2; - int me, owner, *lock = lockptr; me = gettid(); owner = 0; if (!_lockcmpxchgp(lock, &owner, me)) { if (owner == me) { - kprintf("%s:%d: warning: lock re-entry on %s in %s()\n", file, line, + kprintf("%s:%d: error: lock re-entry on %s in %s()\n", file, line, lockname, func); + if (weaken(__die)) weaken(__die)(); + __restorewintty(); _Exit(1); } i = 0; diff --git a/libc/intrin/_spinlock_debug_1.c b/libc/intrin/_trylock_debug_4.c similarity index 67% rename from libc/intrin/_spinlock_debug_1.c rename to libc/intrin/_trylock_debug_4.c index 126e47099..901548ce3 100644 --- a/libc/intrin/_spinlock_debug_1.c +++ b/libc/intrin/_trylock_debug_4.c @@ -16,41 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/bits/weaken.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchgp.h" #include "libc/intrin/spinlock.h" -#include "libc/nexgen32e/rdtsc.h" -#include "libc/time/clockstonanos.internal.h" +#include "libc/log/log.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" -void _spinlock_debug_1(void *lockptr, const char *lockname, const char *file, - int line, const char *func) { - unsigned i; - uint64_t ts1, ts2; - int me, owner, *lock = lockptr; - me = gettid(); - owner = 0; - if (!_lockcmpxchgp(lock, &owner, me)) { - if (owner == me) { - kprintf("%s:%d: warning: possible re-entry on lock %s in %s()\n", file, - line, lockname, func); - } - i = 0; - ts1 = rdtsc(); - for (;;) { - owner = 0; - if (_lockcmpxchgp(lock, &owner, me)) break; - ts2 = rdtsc(); - if (ClocksToNanos(ts1, ts2) > 1000000000ul) { - ts1 = ts2; - kprintf("%s:%d: warning: slow lock on %s in %s()\n", file, line, - lockname, func); - } - if (++i & 7) { - __builtin_ia32_pause(); - } else { - sched_yield(); - } - } +privileged int _trylock_debug_4(int *lock, const char *lockname, + const char *file, int line, const char *func) { + int owner = 0; + int me = gettid(); + if (_lockcmpxchgp(lock, &owner, me)) { + return 0; + } else if (owner != me) { + return owner; + } else { + kprintf("%s:%d: error: lock re-entry on %s in %s()\n", file, line, lockname, + func); + if (weaken(__die)) weaken(__die)(); + __restorewintty(); + _Exit(1); } } diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index 95a77a7ad..e9950b359 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -20,6 +20,10 @@ #include "libc/dce.h" #include "libc/nexgen32e/threaded.h" #include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/runtime/internal.h" + +__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; /** * Returns current thread id. @@ -38,7 +42,7 @@ privileged int gettid(void) { } if (IsWindows()) { - return GetCurrentThreadId(); + return __imp_GetCurrentThreadId(); } if (IsLinux()) { @@ -83,5 +87,5 @@ privileged int gettid(void) { return wut; // narrowing intentional } - return getpid(); + return __pid; } diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 7e10b36e9..e7b05d974 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -74,10 +74,23 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \ -fno-sanitize=all \ -fno-stack-protector +# we can't use compiler magic because: +# spinlocks are called very early in initialization +# e.g. __cxa_atexit() +o/$(MODE)/libc/intrin/gettid.greg.o \ +o/$(MODE)/libc/intrin/_trylock_debug_4.o \ +o/$(MODE)/libc/intrin/_spinlock_debug_4.o: \ + OVERRIDE_CFLAGS += \ + -fwrapv \ + -x-no-pg \ + -mno-fentry \ + -ffreestanding \ + -fno-sanitize=all \ + -fno-stack-protector + o/$(MODE)/libc/intrin/tls.greg.o \ o/$(MODE)/libc/intrin/exit.greg.o \ o/$(MODE)/libc/intrin/exit1.greg.o \ -o/$(MODE)/libc/intrin/gettid.greg.o \ o/$(MODE)/libc/intrin/getenv.greg.o \ o/$(MODE)/libc/intrin/assertfail.greg.o \ o/$(MODE)/libc/intrin/describeiov.greg.o \ diff --git a/libc/intrin/once.h b/libc/intrin/once.h index 52eb74f2d..8aa755ad9 100644 --- a/libc/intrin/once.h +++ b/libc/intrin/once.h @@ -2,20 +2,20 @@ #define COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ #include "libc/intrin/spinlock.h" -#define _once(x) \ - ({ \ - typeof(x) oncerc; \ - static bool once; \ - static typeof(oncerc) onceresult; \ - _Alignas(64) static char oncelock; \ - _spinlock(&oncelock); \ - if (once) { \ - oncerc = onceresult; \ - } else { \ - oncerc = onceresult = x; \ - } \ - _spunlock(&oncelock); \ - oncerc; \ +#define _once(x) \ + ({ \ + typeof(x) oncerc; \ + static bool once; \ + static typeof(oncerc) onceresult; \ + _Alignas(64) static int oncelock; \ + _spinlock(&oncelock); \ + if (once) { \ + oncerc = onceresult; \ + } else { \ + oncerc = onceresult = x; \ + } \ + _spunlock(&oncelock); \ + oncerc; \ }) #endif /* COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ */ diff --git a/libc/intrin/releasefd.c b/libc/intrin/releasefd.c index 660e24fcd..ef08101db 100644 --- a/libc/intrin/releasefd.c +++ b/libc/intrin/releasefd.c @@ -20,13 +20,6 @@ #include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" -void __releasefd_unlocked(int fd) { - if (0 <= fd && fd < g_fds.n) { - g_fds.p[fd].kind = 0; - g_fds.f = MIN(fd, g_fds.f); - } -} - void __releasefd(int fd) { _spinlock(&__fds_lock); __releasefd_unlocked(fd); diff --git a/libc/intrin/releasefd_unlocked.c b/libc/intrin/releasefd_unlocked.c new file mode 100644 index 000000000..6be6460b8 --- /dev/null +++ b/libc/intrin/releasefd_unlocked.c @@ -0,0 +1,27 @@ +/*-*- 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/macros.internal.h" + +void __releasefd_unlocked(int fd) { + if (0 <= fd && fd < g_fds.n) { + g_fds.p[fd].kind = 0; + g_fds.f = MIN(fd, g_fds.f); + } +} diff --git a/libc/intrin/sched_yield.S b/libc/intrin/sched_yield.S index 9f6059da9..1060780c4 100644 --- a/libc/intrin/sched_yield.S +++ b/libc/intrin/sched_yield.S @@ -19,6 +19,7 @@ #include "libc/dce.h" #include "libc/sysv/consts/nr.h" #include "libc/macros.internal.h" +.privileged // Asks kernel to let other threads be scheduled. // diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index 0b6d9eb8c..50b2761ad 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -4,6 +4,10 @@ #include "libc/calls/calls.h" #include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/lockcmpxchgp.h" +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § spinlocks ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│─╝ + privileged unsophisticated locking subroutines */ #if IsModeDbg() && !defined(_SPINLOCK_DEBUG) #define _SPINLOCK_DEBUG @@ -12,15 +16,27 @@ #if defined(_SPINLOCK_DEBUG) #define _spinlock(lock) _spinlock_ndebug(lock) #define _spinlock_ndebug(lock) _spinlock_cooperative(lock) +#define _trylock(lock) _trylock_debug(lock) +#define _seizelock(lock) _seizelock_impl(lock, gettid()) #elif defined(TINY) #define _spinlock(lock) _spinlock_tiny(lock) #define _spinlock_ndebug(lock) _spinlock_tiny(lock) +#define _trylock(lock) _trylock_inline(lock) +#define _seizelock(lock) _seizelock_impl(lock, 1) #else #define _spinlock(lock) _spinlock_cooperative(lock) #define _spinlock_ndebug(lock) _spinlock_cooperative(lock) +#define _trylock(lock) _trylock_inline(lock) +#define _seizelock(lock) _seizelock_impl(lock, 1) #endif -#define _trylock(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST) +#define _trylock_inline(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST) + +#define _trylock_debug(lock) \ + _trylock_debug_4(lock, #lock, __FILE__, __LINE__, __FUNCTION__) + +#define _spinlock_debug(lock) \ + _spinlock_debug_4(lock, #lock, __FILE__, __LINE__, __FUNCTION__) #define _spunlock(lock) \ do { \ @@ -29,19 +45,19 @@ __atomic_store(__lock, &__x, __ATOMIC_RELAXED); \ } while (0) -#define _seizelock(lock) \ +#define _seizelock_impl(lock, value) \ do { \ autotype(lock) __lock = (lock); \ - typeof(*__lock) __x = 1; \ + typeof(*__lock) __x = (value); \ __atomic_store(__lock, &__x, __ATOMIC_RELEASE); \ } while (0) -#define _spinlock_tiny(lock) \ - do { \ - autotype(lock) __lock = (lock); \ - while (_trylock(__lock)) { \ - __builtin_ia32_pause(); \ - } \ +#define _spinlock_tiny(lock) \ + do { \ + autotype(lock) __lock = (lock); \ + while (_trylock_inline(__lock)) { \ + __builtin_ia32_pause(); \ + } \ } while (0) #define _spinlock_cooperative(lock) \ @@ -51,7 +67,7 @@ int __tries = 0; \ for (;;) { \ __atomic_load(__lock, &__x, __ATOMIC_RELAXED); \ - if (!__x && !_trylock(__lock)) { \ + if (!__x && !_trylock_inline(__lock)) { \ break; \ } else if (++__tries & 7) { \ __builtin_ia32_pause(); \ @@ -61,21 +77,7 @@ } \ } while (0) -void _spinlock_debug_1(void *, const char *, const char *, int, const char *); -void _spinlock_debug_4(void *, const char *, const char *, int, const char *); - -#define _spinlock_debug(lock) \ - do { \ - switch (sizeof(*(lock))) { \ - case 1: \ - _spinlock_debug_1(lock, #lock, __FILE__, __LINE__, __FUNCTION__); \ - break; \ - case 4: \ - _spinlock_debug_4(lock, #lock, __FILE__, __LINE__, __FUNCTION__); \ - break; \ - default: \ - assert(!"unsupported size"); \ - } \ - } while (0) +int _trylock_debug_4(int *, const char *, const char *, int, const char *); +void _spinlock_debug_4(int *, const char *, const char *, int, const char *); #endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */ diff --git a/libc/intrin/virtualprotect.c b/libc/intrin/virtualprotect.c index 07fb6f747..a71842dc5 100644 --- a/libc/intrin/virtualprotect.c +++ b/libc/intrin/virtualprotect.c @@ -24,6 +24,11 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; +static const char *DescribeVpFlags(uint32_t *x) { + if (!x) return "n/a"; + return DescribeNtPageFlags(*x); +} + /** * Protects memory on the New Technology. * @note this wrapper takes care of ABI, STRACE(), and __winerr() @@ -34,13 +39,9 @@ textwindows bool32 VirtualProtect(void *lpAddress, uint64_t dwSize, bool32 bOk; char oldbuf[64]; bOk = __imp_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); - if (bOk) { - __stpcpy(oldbuf, DescribeNtPageFlags(*lpflOldProtect)); - } else { - __winerr(); - __stpcpy(oldbuf, "n/a"); - } + if (!bOk) __winerr(); NTTRACE("VirtualProtect(%p, %'zu, %s, [%s]) → %hhhd% m", lpAddress, dwSize, - DescribeNtPageFlags(flNewProtect), oldbuf, bOk); + DescribeNtPageFlags(flNewProtect), + DescribeVpFlags(bOk ? lpflOldProtect : 0), bOk); return bOk; } diff --git a/libc/nexgen32e/threaded.h b/libc/nexgen32e/threaded.h index 0b6a61b0b..778da129d 100644 --- a/libc/nexgen32e/threaded.h +++ b/libc/nexgen32e/threaded.h @@ -23,9 +23,9 @@ void __install_tls(char[hasatleast 64]); static noasan inline char *__get_tls(void) { char *tib, *lin = (char *)0x30; if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) { - asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin)); + asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory"); } else { - asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin)); + asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory"); if (IsWindows()) { tib = *(char **)(tib + 0x1480 + __tls_index * 8); } diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index 6d24f19ee..43e7c940d 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -31,7 +31,7 @@ static int thepid; static uint128_t thepool; -_Alignas(64) static char rand64_lock; +_Alignas(64) static int rand64_lock; /** * Returns nondeterministic random data. diff --git a/libc/runtime/cosmo.S b/libc/runtime/cosmo.S index c14c2a1c3..97f228013 100644 --- a/libc/runtime/cosmo.S +++ b/libc/runtime/cosmo.S @@ -135,6 +135,16 @@ cosmo: push %rbp .init.end 306,_init_ftrace #endif +#if IsAsan() + .init.start 306,_init_symbols + push %rdi + push %rsi + call __init_symbols + pop %rsi + pop %rdi + .init.end 306,_init_symbols +#endif + #if IsModeDbg() #ifdef SYSDEBUG .init.start 307,_init_printargs diff --git a/libc/runtime/getsymboltable.c b/libc/runtime/getsymboltable.c index fa0358e36..cc7988072 100644 --- a/libc/runtime/getsymboltable.c +++ b/libc/runtime/getsymboltable.c @@ -30,7 +30,7 @@ #include "libc/zip.h" #include "libc/zipos/zipos.internal.h" -static char g_lock; +static int g_lock; hidden struct SymbolTable *__symtab; // for kprintf /** diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index 1e9c5f7b0..61634c593 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -59,6 +59,7 @@ privileged noinstrument noasan int __hook(void *ifunc, intptr_t kMcount = (intptr_t)&mcount; intptr_t kProgramCodeStart = (intptr_t)_ereal; intptr_t kPrivilegedStart = (intptr_t)__privileged_addr; + if (!symbols) return -1; __morph_begin(); for (i = 0; i < symbols->count; ++i) { if (symbols->addr_base + symbols->symbols[i].x < kProgramCodeStart) { diff --git a/libc/runtime/symbols.c b/libc/runtime/symbols.c new file mode 100644 index 000000000..846eba41e --- /dev/null +++ b/libc/runtime/symbols.c @@ -0,0 +1,30 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" +#include "libc/dce.h" +#include "libc/log/backtrace.internal.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" + +void __init_symbols(void) { + if (__strace || (IsAsan() && weaken(__die))) { + GetSymbolTable(); + } +} diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index d0bd84f4d..42b5d937b 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/files.h" #include "libc/nt/struct/pollfd.h" @@ -44,7 +45,9 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize, for (;;) { if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1, __SIG_POLLING_INTERVAL_MS)) { - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) { + return eintr(); + } continue; } if ((h = WSAAccept(fd->handle, addr, (int32_t *)addrsize, 0, 0)) != -1) { @@ -54,7 +57,8 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize, if ((!(flags & SOCK_NONBLOCK) || __sys_ioctlsocket_nt(h, FIONBIO, (uint32_t[]){1}) != -1) && (sockfd2 = calloc(1, sizeof(struct SockFd)))) { - if ((client = __reservefd(-1)) != -1) { + _spinlock(&__fds_lock); + if ((client = __reservefd_unlocked(-1)) != -1) { sockfd2->family = sockfd->family; sockfd2->type = sockfd->type; sockfd2->protocol = sockfd->protocol; @@ -63,8 +67,10 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize, g_fds.p[client].mode = 0140666; g_fds.p[client].handle = h; g_fds.p[client].extra = (uintptr_t)sockfd2; + _spunlock(&__fds_lock); return client; } + _spunlock(&__fds_lock); free(sockfd2); } __sys_closesocket_nt(h); diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c index 99e508734..c0e174ec4 100644 --- a/libc/sock/epoll.c +++ b/libc/sock/epoll.c @@ -36,6 +36,7 @@ #include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" @@ -1324,7 +1325,8 @@ static textwindows dontinline int sys_epoll_create1_nt(uint32_t flags) { struct PortState *port_state; struct TsTreeNode *tree_node; if (wepoll_init() < 0) return -1; - if ((fd = __reservefd(-1)) == -1) return -1; + fd = __reservefd(-1); + if (fd == -1) return -1; port_state = port_new(&ephnd); if (!port_state) { __releasefd(fd); @@ -1338,10 +1340,12 @@ static textwindows dontinline int sys_epoll_create1_nt(uint32_t flags) { __releasefd(fd); return -1; } + _spinlock(&__fds_lock); g_fds.p[fd].kind = kFdEpoll; g_fds.p[fd].handle = ephnd; g_fds.p[fd].flags = flags; g_fds.p[fd].mode = 0140666; + _spunlock(&__fds_lock); return fd; } diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index 913b66e14..5c069acbf 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -44,7 +44,8 @@ textwindows int sys_socket_nt(int family, int type, int protocol) { int64_t h; struct SockFd *sockfd; int fd, oflags, truetype; - if ((fd = __reservefd(-1)) == -1) return -1; + fd = __reservefd(-1); + if (fd == -1) return -1; truetype = type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK); if ((h = WSASocket(family, truetype, protocol, NULL, 0, kNtWsaFlagOverlapped)) != -1) { diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index 44a63c7b9..7ee42b8e9 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -30,10 +30,10 @@ #include "libc/sysv/errfuns.h" textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { - int64_t hpipe, h1, h2; - int reader, writer, oflags; - char16_t pipename[64]; uint32_t mode; + char16_t pipename[64]; + int64_t hpipe, h1, h2; + int rc, reader, writer, oflags; // Supports only AF_UNIX if (family != AF_UNIX) { @@ -53,9 +53,13 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { } CreatePipeName(pipename); - if ((reader = __reservefd(-1)) == -1) return -1; - if ((writer = __reservefd(-1)) == -1) { - __releasefd(reader); + _spinlock(&__fds_lock); + reader = __reservefd_unlocked(-1); + writer = __reservefd_unlocked(-1); + _spunlock(&__fds_lock); + if (reader == -1 || writer == -1) { + if (reader != -1) __releasefd(reader); + if (writer != -1) __releasefd(writer); return -1; } if ((hpipe = CreateNamedPipe( @@ -68,28 +72,33 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { h1 = CreateFile(pipename, kNtGenericWrite | kNtGenericRead, 0, &kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0); - if (h1 == -1) { - CloseHandle(hpipe); - __releasefd(writer); - __releasefd(reader); - return -1; - } _spinlock(&__fds_lock); - g_fds.p[reader].kind = kFdFile; - g_fds.p[reader].flags = oflags; - g_fds.p[reader].mode = 0140444; - g_fds.p[reader].handle = hpipe; + if (h1 != -1) { - g_fds.p[writer].kind = kFdFile; - g_fds.p[writer].flags = oflags; - g_fds.p[writer].mode = 0140222; - g_fds.p[writer].handle = h1; + g_fds.p[reader].kind = kFdFile; + g_fds.p[reader].flags = oflags; + g_fds.p[reader].mode = 0140444; + g_fds.p[reader].handle = hpipe; + + g_fds.p[writer].kind = kFdFile; + g_fds.p[writer].flags = oflags; + g_fds.p[writer].mode = 0140222; + g_fds.p[writer].handle = h1; + + sv[0] = reader; + sv[1] = writer; + + rc = 0; + } else { + CloseHandle(hpipe); + __releasefd_unlocked(writer); + __releasefd_unlocked(reader); + rc = -1; + } _spunlock(&__fds_lock); - sv[0] = reader; - sv[1] = writer; - return 0; + return rc; } diff --git a/libc/zipos/get.c b/libc/zipos/get.c index 93a067eeb..de5f4e14c 100644 --- a/libc/zipos/get.c +++ b/libc/zipos/get.c @@ -75,7 +75,7 @@ struct Zipos *__zipos_get(void) { const char *progpath; static struct Zipos zipos; uint8_t *map, *base, *cdir; - _Alignas(64) static char lock; + _Alignas(64) static int lock; _spinlock(&lock); if (!once) { sigfillset(&neu); diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 9e8fcf4d9..7a5401e86 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -25,6 +25,7 @@ #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" @@ -126,11 +127,14 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags, if (h->mem) { if ((fd = IsWindows() ? __reservefd(-1) : dup(2)) != -1) { if (__ensurefds(fd) != -1) { + _spinlock(&__fds_lock); h->handle = g_fds.p[fd].handle; g_fds.p[fd].kind = kFdZip; g_fds.p[fd].handle = (intptr_t)h; g_fds.p[fd].flags = flags | O_CLOEXEC; g_fds.p[fd].mode = mode; + g_fds.p[fd].extra = 0; + _spunlock(&__fds_lock); return fd; } close(fd); diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index ddfbae830..6362619f0 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/macros.internal.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -88,3 +90,26 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) { EXPECT_STREQ("hello", buf); EXPECT_SYS(0, 0, close(3)); } + +int CountFds(void) { + int i, count; + for (count = i = 0; i < g_fds.n; ++i) { + if (g_fds.p[i].kind) { + ++count; + } + } + return count; +} + +TEST(open, lotsOfFds) { + if (!IsWindows()) return; + int i, n = 200; + ASSERT_SYS(0, 0, xbarf("hello.txt", "hello", -1)); + for (i = 3; i < n; ++i) { + EXPECT_EQ(i, CountFds()); + EXPECT_SYS(0, i, open("hello.txt", O_RDONLY)); + } + for (i = 3; i < n; ++i) { + EXPECT_SYS(0, 0, close(i)); + } +} diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index 232d5a48a..fda45026a 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -36,7 +36,8 @@ #include "libc/time/time.h" char *stack, *tls; -int x, me, tid, thechilde, childetid; +int x, me, tid, *childetid; +_Atomic(int) thechilde; void SetUp(void) { x = 0; @@ -44,6 +45,7 @@ void SetUp(void) { tls = calloc(1, 64); __initialize_tls(tls); *(int *)(tls + 0x3c) = 31337; + childetid = (int *)(tls + 0x0038); ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0))); } @@ -77,26 +79,25 @@ int CloneTest1(void *arg) { ASSERT_EQ(31337, errno); } ASSERT_EQ(23, (intptr_t)arg); - thechilde = gettid(); ASSERT_NE(gettid(), getpid()); - ASSERT_EQ(gettid(), childetid); // CLONE_CHILD_SETTID + ASSERT_EQ(gettid(), *childetid); // CLONE_CHILD_SETTID return 0; } TEST(clone, test1) { int ptid = 0; - _seizelock(&childetid); + *childetid = -1; + _seizelock(childetid); ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS, - (void *)23, &ptid, tls, 64, &childetid))); - _spinlock(&childetid); // CLONE_CHILD_CLEARTID + (void *)23, &ptid, tls, 64, childetid))); + _spinlock(childetid); // CLONE_CHILD_CLEARTID ASSERT_EQ(tid, ptid); ASSERT_EQ(42, x); ASSERT_NE(me, tid); - ASSERT_EQ(tid, thechilde); ASSERT_EQ(0, errno); errno = 31337; ASSERT_EQ(31337, errno); @@ -115,13 +116,13 @@ int CloneTestSys(void *arg) { TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { ASSERT_EQ(0, errno); ASSERT_EQ(31337, *(int *)(tls + 0x3c)); - _seizelock(&childetid); + _seizelock(childetid); ASSERT_NE(-1, (tid = clone(CloneTestSys, stack, GetStackSize(), CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS, - (void *)23, 0, tls, 64, &childetid))); - _spinlock(&childetid); // CLONE_CHILD_CLEARTID + (void *)23, 0, tls, 64, childetid))); + _spinlock(childetid); // CLONE_CHILD_CLEARTID ASSERT_EQ(0, errno); ASSERT_EQ(EFAULT, *(int *)(tls + 0x3c)); } From db0d8dd806c5ef10b8f92f595d17b2e0e4e7861d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 21 May 2022 07:52:58 -0700 Subject: [PATCH 28/40] Support Linux binfmt_misc and APE loading on Apple The "no modify self" variant of Actually Portable Executable is now supported on all platforms. If you use `$(APE_NO_MODIFY_SELF)` then ld.bfd will embed a 4096 byte ELF binary and a 4096 byte Macho file which are installed on the fly to ${TMPDIR:-/tmp}, which enables us launch the executable, without needing to copy the whole executable To prevent it from copying a tiny executable to your temp directory you need to install the `ape` command (renamed from ape-loader), to a system path. For example: # FreeBSD / NetBSD / OpenBSD make -j8 o//ape/ape cp o//ape/ape /usr/bin/ape # Mac OS # make -j8 o//ape/ape.macho curl https://justine.lol/ape.macho >/usr/bin/ape chmod +x /usr/bin/ape On Linux you can get even more performance with the new binfmt_misc support which makes launching non-modifying APE binaries as fast as launching ELF executables. Running the following command: # Linux ape/apeinstall.sh Will copy APE loader to /usr/bin/ape and register with binfmt_misc Lastly, this change also fixes a really interesting race condition with OpenBSD thread joining. --- ape/ape.S | 39 +- ape/ape.lds | 11 + ape/ape.mk | 57 +-- ape/apeinstall.sh | 63 +++ ape/{loader1.S => loader-elf.S} | 53 ++- ape/loader-macho.S | 127 ++++++ ape/loader-macho.lds | 43 ++ ape/loader.c | 708 ++++++++++++++++++++++++-------- ape/loader.h | 21 + ape/loader.lds | 9 + build/bootstrap/ape | Bin 0 -> 4096 bytes build/sanitycheck | 11 +- build/sanitycheck2 | 11 +- examples/examples.mk | 8 + examples/mkhello.c | 17 + examples/nomodifyself.c | 36 ++ libc/calls/mman.greg.c | 2 +- libc/calls/nanos.c | 34 ++ libc/calls/nanos.h | 10 + libc/calls/sig2.c | 5 +- libc/crt/crt.S | 21 +- libc/fmt/leb128.h | 2 +- libc/fmt/zleb64.c | 2 +- libc/runtime/clone.c | 18 +- libc/runtime/ftracer.c | 4 +- libc/testlib/ugly.h | 6 +- test/libc/calls/execve_test.c | 24 +- test/libc/str/test.mk | 24 +- third_party/make/config.h | 2 +- third_party/make/dir.c | 14 - third_party/smallz4/smallz4.hh | 12 +- 31 files changed, 1089 insertions(+), 305 deletions(-) create mode 100755 ape/apeinstall.sh rename ape/{loader1.S => loader-elf.S} (71%) create mode 100644 ape/loader-macho.S create mode 100644 ape/loader-macho.lds create mode 100644 ape/loader.h create mode 100644 build/bootstrap/ape create mode 100644 examples/mkhello.c create mode 100644 examples/nomodifyself.c create mode 100644 libc/calls/nanos.c create mode 100644 libc/calls/nanos.h diff --git a/ape/ape.S b/ape/ape.S index 48e25aaae..acddc38a0 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -542,27 +542,35 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // present two choices. .ascii "o=\"$(command -v \"$0\")\"\n" // Try to use a system-wide APE loader. - .ascii "type ape-loader >/dev/null 2>&1 && " - .ascii "exec ape-loader \"$o\" \"$@\"\n" + .ascii "type ape >/dev/null 2>&1 && " + .ascii "exec ape \"$o\" \"$@\"\n" #ifdef APE_LOADER // There is no system-wide APE loader, but there is one // embedded inside the APE. So if the system is not MacOs, // extract the loader into a temp folder, and use it to // load the APE without modifying it. - .ascii "if [ ! -d /Applications ]; then\n" - .ascii "t=\"${TMPDIR:-/tmp}/ape-loader\"\n" - .ascii "[ -x \"$t\" ] || {\n" + .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" + .ascii "if [ ! -x \"$t\" ]; then\n" + .ascii "if [ ! -d /Applications ]; then\n" .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" .shstub ape_loader_dd_skip,2 .ascii "\" count=\"" .shstub ape_loader_dd_count,2 - .ascii "\" bs=64 2>/dev/null &&\n" - .ascii "chmod 755 \"$t.$$\" &&\n" - .ascii "mv \"$t.$$\" \"$t\"\n" - .ascii "}\n" - .ascii "exec \"$t\" \"$o\" \"$@\"\n" + .ascii "\" bs=64\n" +#if SupportsXnu() && defined(APE_LOADER_MACHO) + .ascii "else\n" + .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" + .shstub ape_loader_macho_dd_skip,2 + .ascii "\" count=\"" + .shstub ape_loader_macho_dd_count,2 + .ascii "\" bs=64\n" +#endif /* APE_LOADER_MACHO */ + .ascii "fi 2>/dev/null &&\n" + .ascii "chmod 755 \"$t.$$\" &&\n" + .ascii "mv \"$t.$$\" \"$t\"\n" .ascii "fi\n" -#endif + .ascii "exec \"$t\" \"$o\" \"$@\"\n" +#endif /* APE_LOADER */ #ifndef APE_NO_MODIFY_SELF // The default behavior is: to overwrite the header in place. // We prefer this because it's a tiny constant one time cost. @@ -656,6 +664,11 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .incbin APE_LOADER .previous #endif /* APE_LOADER */ +#if SupportsXnu() && defined(APE_LOADER_MACHO) + .section .ape.loader-macho,"a",@progbits + .incbin APE_LOADER_MACHO + .previous +#endif /* APE_LOADER_MACHO */ #endif /* SupportsWindows() || SupportsMetal() || SupportsXnu() */ #if SupportsSystemv() || SupportsMetal() @@ -856,7 +869,7 @@ ape_macho: .long (520f-510f)/4 # count 510: .quad 0 # rax .quad IMAGE_BASE_VIRTUAL # rbx - .quad 0 # rcx + .quad XNU # rcx .quad 0 # rdx .quad 0 # rdi .quad 0 # rsi @@ -870,7 +883,7 @@ ape_macho: .quad 0 # r13 .quad 0 # r14 .quad 0 # r15 - .quad _xnu # rip + .quad _start # rip .quad 0 # rflags .quad 0 # cs .quad 0 # fs diff --git a/ape/ape.lds b/ape/ape.lds index 4e00ad30d..f37234a7b 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -397,6 +397,12 @@ SECTIONS { KEEP(*(.ape.loader)) . = ALIGN(64); HIDDEN(ape_loader_end = .); +#if SupportsXnu() + HIDDEN(ape_loader_macho = .); + KEEP(*(.ape.loader-macho)) + . = ALIGN(64); + HIDDEN(ape_loader_macho_end = .); +#endif } /*END: payload */ /*BEGIN: bss memory void */ @@ -549,6 +555,11 @@ HIDDEN(ape_bss_align = PAGESIZE); SHSTUB2(ape_loader_dd_skip, RVA(ape_loader) / 64); SHSTUB2(ape_loader_dd_count, (ape_loader_end - ape_loader) / 64); +#if SupportsXnu() +SHSTUB2(ape_loader_macho_dd_skip, RVA(ape_loader_macho) / 64); +SHSTUB2(ape_loader_macho_dd_count, (ape_loader_macho_end - ape_loader_macho) / 64); +#endif + #if SupportsXnu() SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8); SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8); diff --git a/ape/ape.mk b/ape/ape.mk index 6302a2804..590075703 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -15,18 +15,18 @@ PKGS += APE -APE = o/$(MODE)/ape/ape.o \ +APE = o/$(MODE)/ape/ape.o \ o/$(MODE)/ape/ape.lds -APE_NO_MODIFY_SELF = \ - o/$(MODE)/ape/ape.lds \ +APE_NO_MODIFY_SELF = \ + o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape-no-modify-self.o -APELINK = \ - $(COMPILE) \ - -ALINK.ape \ - $(LINK) \ - $(LINKARGS) \ +APELINK = \ + $(COMPILE) \ + -ALINK.ape \ + $(LINK) \ + $(LINKARGS) \ $(OUTPUT_OPTION) APE_FILES := $(wildcard ape/*.*) @@ -38,32 +38,43 @@ APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S) APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o) APE_CHECKS = $(APE_HDRS:%=o/%.ok) -o/$(MODE)/ape/ape.lds: \ - ape/ape.lds \ - ape/macros.internal.h \ - libc/dce.h \ +o/$(MODE)/ape/ape.lds: \ + ape/ape.lds \ + ape/macros.internal.h \ + libc/dce.h \ libc/zip.h -o/ape/idata.inc: \ - ape/idata.internal.h \ +o/ape/idata.inc: \ + ape/idata.internal.h \ ape/relocations.h -o/$(MODE)/ape/ape-no-modify-self.o: ape/ape.S o/$(MODE)/ape/loader.elf - @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/loader.elf\"" -DAPE_NO_MODIFY_SELF $< +o/$(MODE)/ape/ape-no-modify-self.o: \ + ape/ape.S \ + o/$(MODE)/ape/ape \ + o/$(MODE)/ape/ape.macho + @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/ape\"" -DAPE_LOADER_MACHO="\"o/$(MODE)/ape/ape.macho\"" $< o/$(MODE)/ape/loader.o: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -fpie -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $< + @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $< o/$(MODE)/ape/loader-gcc.asm: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $< + @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $< -o/$(MODE)/ape/loader.elf: \ - o/$(MODE)/ape/loader.o \ - o/$(MODE)/ape/loader1.o \ +o/$(MODE)/ape/ape: \ + o/$(MODE)/ape/loader.o \ + o/$(MODE)/ape/loader-elf.o \ ape/loader.lds @$(ELFLINK) -s -z max-page-size=0x10 +o/$(MODE)/ape/ape.macho: \ + o/$(MODE)/ape/loader.o \ + o/$(MODE)/ape/loader-macho.o \ + ape/loader-macho.lds + @$(ELFLINK) -s -z max-page-size=0x10 + .PHONY: o/$(MODE)/ape -o/$(MODE)/ape: $(APE) \ - $(APE_CHECKS) \ +o/$(MODE)/ape: $(APE) \ + $(APE_CHECKS) \ + o/$(MODE)/ape/ape \ + o/$(MODE)/ape/ape.macho \ o/$(MODE)/ape/ape-no-modify-self.o diff --git a/ape/apeinstall.sh b/ape/apeinstall.sh new file mode 100755 index 000000000..d5723f130 --- /dev/null +++ b/ape/apeinstall.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +if ! [ x"$(uname -s)" = xLinux ]; then + echo this script is intended for linux binfmt_misc >&2 + echo freebsd/netbsd/openbsd users can use release binary >&2 + exit 1 +fi + +if [ -f o/depend ]; then + # mkdeps.com build was successfully run so assume we can build + echo >&2 + echo running: make -j8 o//ape/ape >&2 + make -j8 o//ape/ape || exit + echo done >&2 +else + # no evidence we can build, use prebuilt one + mkdir -p o//ape || exit + cp -af build/bootstrap/ape o//ape/ape +fi + +echo >&2 +echo installing o//ape/ape to /usr/bin/ape >&2 +echo sudo mv -f o//ape/ape /usr/bin/ape >&2 +sudo mv -f o//ape/ape /usr/bin/ape || exit +echo done >&2 + +if [ -e /proc/sys/fs/binfmt_misc/APE ]; then + echo >&2 + echo it looks like APE is already registered with binfmt_misc >&2 + echo please check that it is mapped to ape not /bin/sh >&2 + echo cat /proc/sys/fs/binfmt_misc/APE >&2 + cat /proc/sys/fs/binfmt_misc/APE >&2 + # TODO: we need better uninstall recommendations + # the following works fine for justine + # but might remove unrelated software? + # sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status' + exit +fi + +if ! [ -e /proc/sys/fs/binfmt_misc ]; then + echo >&2 + echo loading binfmt_misc into your kernel >&2 + echo you may need to edit configs to persist across reboot >&2 + echo sudo modprobe binfmt_misc >&2 + sudo modprobe binfmt_misc || exit + echo done >&2 +fi + +if ! [ -e /proc/sys/fs/binfmt_misc/register ]; then + echo >&2 + echo mounting binfmt_misc into your kernel >&2 + echo you may need to edit configs to persist across reboot >&2 + echo sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc >&2 + sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc || exit + echo done >&2 +fi + +echo >&2 +echo registering APE with binfmt_misc >&2 +echo you may need to edit configs to persist across reboot >&2 +echo 'sudo sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2 +sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit +echo done >&2 diff --git a/ape/loader1.S b/ape/loader-elf.S similarity index 71% rename from ape/loader1.S rename to ape/loader-elf.S index 3114ffdc4..9fafc4fd0 100644 --- a/ape/loader1.S +++ b/ape/loader-elf.S @@ -38,20 +38,54 @@ ehdr: .ascii "\177ELF" .long 0 # e_flags .word 64 # e_ehsize .word 56 # e_phentsize - .word 3 # e_phnum + .word 4 # e_phnum .word 0 # e_shentsize .word 0 # e_shnum .word 0 # e_shstrndx .endobj ehdr,globl -// memcpy(0x200000, loader); xor %eax,%eax; jmp 0x200000 +// Ape Loader Entrpoint +// +// This is normally called by the operating system. However it may +// be called by the Actually Portable Executables themselves, when +// re-executing a program. Just do this: +// +// memcpy(0x200000, loader) +// xor %eax,%eax +// inc %eax +// jmp 0x200000 +// +// @see APE_LOADER_ENTRY jg47h: .org 0x47 .endobj jg47h - _start: mov %rsp,%rsi - jmp loader + jmp ApeLoader .endfn _start,globl +// System Call Entrpoint +// +// This function is used by the APE loader to make system calls. +// We also pass a reference to this function to the APE binary's +// _start() function. It's needed because on OpenBSD, msyscall() +// restricts which pages can issue system calls, and it can only +// be called once. Therefore if we want to be load and re-load a +// binary multiple times without calling the system execve(), we +// need to be able to handover the SYSCALL function. We hardcode +// this to a fixed address, but that shouldn't be used, since we +// would ideally want to move it to a random page in the future. +// +// @see APE_LOADER_SYSCALL +sc50h: .org 0x50 + .endobj sc50h +__syscall_loader: + clc + syscall + jc 1f + ret +1: neg %rax + ret + .endfn __syscall_loader,globl + .align 8 phdrs: .long PT_LOAD # p_type .long PF_R|PF_X # p_flags @@ -61,6 +95,16 @@ phdrs: .long PT_LOAD # p_type .quad filesz # p_filesz .quad filesz # p_memsz .quad PAGESIZE # p_align + + .long PT_LOAD # p_type + .long PF_R|PF_W # p_flags + .quad 0 # p_offset + .quad bss # p_vaddr + .quad bss # p_paddr + .quad 0 # p_filesz + .quad bsssize # p_memsz + .quad PAGESIZE # p_align + .long PT_GNU_STACK # p_type .long PF_R|PF_W # p_flags .quad 0 # p_offset @@ -69,6 +113,7 @@ phdrs: .long PT_LOAD # p_type .quad 0 # p_filesz .quad 0 # p_memsz .quad 16 # p_align + .long PT_NOTE # p_type .long PF_R # p_flags .quad note - ehdr # p_offset diff --git a/ape/loader-macho.S b/ape/loader-macho.S new file mode 100644 index 000000000..656a05b87 --- /dev/null +++ b/ape/loader-macho.S @@ -0,0 +1,127 @@ +/*-*- 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 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/macho.internal.h" +#include "libc/sysv/consts/prot.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" + +// APE Loader Executable Structure for XNU + + .align 4096 +macho: .long 0xFEEDFACE+1 + .long MAC_CPU_NEXGEN32E + .long MAC_CPU_NEXGEN32E_ALL + .long MAC_EXECUTE + .long 5 # number of load commands + .long 60f-10f # size of all load commands + .long MAC_NOUNDEFS # flags + .long 0 # reserved +10: .long MAC_LC_SEGMENT_64 + .long 20f-10b # unmaps first page dir + .ascin "__PAGEZERO",16 # consistent with linux + .quad 0,0x200000,0,0 # which forbids mem <2m + .long 0,0,0,0 +20: .long MAC_LC_SEGMENT_64 + .long 30f-20b + .ascin "__TEXT",16 + .quad macho # vaddr + .quad filesz # memsz + .quad 0 # file offset + .quad filesz # file size + .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot + .long PROT_EXEC|PROT_READ # initprot + .long 1 # segment section count + .long 0 # flags +210: .ascin "__text",16 # section name (.text) + .ascin "__TEXT",16 + .quad _start # vaddr + .quad textsz # memsz + .long textoff # offset + .long 3 # align 2**3 = 8 + .long 0 # reloc table offset + .long 0 # relocation count + .long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes + .long 0,0,0 # reserved +30: .long MAC_LC_SEGMENT_64 + .long 40f-30b + .ascin "__DATA",16 + .quad bss # vaddr + .quad bsssize # memsz + .quad 0 # offset + .quad 0 # file size + .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot + .long PROT_READ|PROT_WRITE # initprot + .long 1 # segment section count + .long 0 # flags +310: .ascin "__bss",16 # section name (.bss) + .ascin "__DATA",16 + .quad bss # vaddr + .quad bsssize # memsz + .long 0 # offset + .long 12 # align 2**12 = 4096 + .long 0 # reloc table offset + .long 0 # relocation count + .long MAC_S_ZEROFILL # section type & attributes + .long 0,0,0 # reserved +40: .long MAC_LC_UUID + .long 50f-40b + .quad 0x3fb29ee4ac6c87aa # uuid1 + .quad 0xdd2c9bb866d9eef8 # uuid2 +50: .long MAC_LC_UNIXTHREAD + .long 60f-50b # cmdsize + .long MAC_THREAD_NEXGEN32E # flavaflav + .long (520f-510f)/4 # count +510: .quad 0 # rax + .quad 0 # rbx + .quad 0 # rcx + .quad XNU # rdx + .quad 0 # rdi + .quad 0 # rsi + .quad 0 # rbp + .quad 0 # rsp + .quad 0 # r8 + .quad 0 # r9 + .quad 0 # r10 + .quad 0 # r11 + .quad 0 # r12 + .quad 0 # r13 + .quad 0 # r14 + .quad 0 # r15 + .quad _start # rip + .quad 0 # rflags + .quad 0 # cs + .quad 0 # fs + .quad 0 # gs +520: +60: + .endobj macho,globl + + .align 8 +_start: mov %rsp,%rsi + jmp ApeLoader + .endfn _start,globl + +__syscall_loader: + clc + syscall + jc 1f + ret +1: neg %rax + ret + .endfn __syscall_loader,globl diff --git a/ape/loader-macho.lds b/ape/loader-macho.lds new file mode 100644 index 000000000..c8f36b161 --- /dev/null +++ b/ape/loader-macho.lds @@ -0,0 +1,43 @@ +/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ +│vi: set et sts=2 tw=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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +ENTRY(_start) +OUTPUT_FORMAT(binary) + +SECTIONS { + . = 0x200000; + .text : { + *(.text) + *(.rodata .rodata.*) + . = ALIGN(4096); + } + filesz = . - macho; + textsz = . - _start; + .bss ALIGN(4096) : { + bss = .; + *(.bss) + . = ALIGN(4096); + } + memsz = . - macho; + /DISCARD/ : { + *(.*) + } +} + +bsssize = SIZEOF(.bss); +textoff = _start - macho; diff --git a/ape/loader.c b/ape/loader.c index c79b01757..413641fd9 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -16,28 +16,74 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/calls/struct/metastat.internal.h" -#include "libc/calls/struct/stat.h" -#include "libc/elf/def.h" -#include "libc/elf/struct/ehdr.h" -#include "libc/elf/struct/phdr.h" -#include "libc/sysv/consts/prot.h" +#include "ape/loader.h" + +#define TROUBLESHOOT 0 +#define TROUBLESHOOT_OS LINUX /** - * @fileoverview APE embeddable loader for Linux and BSD, e.g. + * @fileoverview APE Loader for GNU/Systemd and FreeBSD/NetBSD/OpenBSD + * + * We recommend using the normal APE design, where binaries assimilate + * themselves once by self-modifying the first 64 bytes. If that can't + * meet your requirements then we provide an excellent alternative. * * m=tiny * make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com - * o/$m/ape/loader.elf o/$m/examples/printargs.com + * o/$m/ape/ape o/$m/examples/printargs.com + * + * This is an embeddable Actually Portable Executable interpreter. The + * `ape/ape.S` bootloader embeds this binary inside each binary that's + * linked using `$(APE_NO_MODIFY_SELF)` so it is an automated seamless + * process. the shell script at the top of the .COM files will copy it + * to `${TMPDIR:-/tmp}/ape` and call execve(). It's a zero copy + * operation in praxis since this payload uses mmap() to load the rest + * of your executable the same way the kernel does, based on ELF phdrs + * which are located in accordance with the first sh printf statement. + * + * APE executables will look for this program on the system path first + * so your APE loader may be installed to your system as follows: + * + * m=tiny + * make -j8 MODE=$m o/$m/ape/ape + * sudo cp o/$m/ape/ape /usr/bin/ape + * + * Your APE loader may be used as a shebang interpreter by doing this: + * + * #!/usr/bin/ape python.com + * # -*- python -*- + * print("hello world") + * + * However you won't need to do that, if your APE Loader is registered + * as a binfmt_misc interpreter. You can do that as follows with root: + * + * sudo cp -f o/$m/ape/ape /usr/bin + * f=/proc/sys/fs/binfmt_misc/register + * sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f" + * + * If the register file doesn't exist on your Linux machine then you'd + * load it using the following commands: + * + * sudo modprobe binfmt_misc + * sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc + * + * You should now experience a performance boost, and you can also now + * use the regular shebang form: + * + * #!/usr/bin/python.com + * # -*- python -*- + * print("hello world") * * @note this can probably be used as a binfmt_misc interpreter */ -#define LINUX 0 -#define FREEBSD 1 -#define NETBSD 2 -#define OPENBSD 3 +#define LINUX 1 +#define METAL 2 +#define WINDOWS 4 +#define XNU 8 +#define OPENBSD 16 +#define FREEBSD 32 +#define NETBSD 64 #define O_RDONLY 0 #define PROT_READ 1 @@ -49,170 +95,394 @@ #define MAP_ANONYMOUS (os == LINUX ? 32 : 4096) #define AT_EXECFN_LINUX 31 #define AT_EXECFN_NETBSD 2014 +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define EM_NEXGEN32E 62 +#define ET_EXEC 2 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define EI_CLASS 4 +#define EI_DATA 5 +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 +#define X_OK 1 +#define XCR0_SSE 2 +#define XCR0_AVX 4 -#define __NR_read (os == LINUX ? 0 : 3) -#define __NR_write (os == LINUX ? 1 : 4) -#define __NR_open (os == LINUX ? 2 : 5) -#define __NR_close (os == LINUX ? 3 : 6) -#define __NR_exit (os == LINUX ? 60 : 1) -#define __NR_mmap (os == LINUX ? 9 : os == FREEBSD ? 477 : 197) -#define __NR_fstat \ - (os == LINUX ? 5 : os == FREEBSD ? 551 : os == OPENBSD ? 53 : 440) +#define Read32(S) \ + ((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \ + (unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000) -static wontreturn void Exit(int os, long rc) { - asm volatile("syscall" - : /* no outputs */ - : "a"(__NR_exit), "D"(rc) - : "memory"); - unreachable; +#define Read64(S) \ + ((unsigned long)(255 & (S)[7]) << 070 | \ + (unsigned long)(255 & (S)[6]) << 060 | \ + (unsigned long)(255 & (S)[5]) << 050 | \ + (unsigned long)(255 & (S)[4]) << 040 | \ + (unsigned long)(255 & (S)[3]) << 030 | \ + (unsigned long)(255 & (S)[2]) << 020 | \ + (unsigned long)(255 & (S)[1]) << 010 | \ + (unsigned long)(255 & (S)[0]) << 000) + +struct PathSearcher { + char os; + unsigned long namelen; + const char *name; + const char *syspath; + char path[1024]; +}; + +struct ElfEhdr { + unsigned char e_ident[16]; + unsigned short e_type; + unsigned short e_machine; + unsigned e_version; + unsigned long e_entry; + unsigned long e_phoff; + unsigned long e_shoff; + unsigned e_flags; + unsigned short e_ehsize; + unsigned short e_phentsize; + unsigned short e_phnum; + unsigned short e_shentsize; + unsigned short e_shnum; + unsigned short e_shstrndx; +}; + +struct ElfPhdr { + unsigned p_type; + unsigned p_flags; + unsigned long p_offset; + unsigned long p_vaddr; + unsigned long p_paddr; + unsigned long p_filesz; + unsigned long p_memsz; + unsigned long p_align; +}; + +static void *syscall; +static struct PathSearcher ps; +extern char __syscall_loader[]; + +static int ToLower(int c) { + return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; } -static void Close(int os, long fd) { +static char *MemCpy(char *d, const char *s, unsigned long n) { + unsigned long i = 0; + for (; i < n; ++i) d[i] = s[i]; + return d + n; +} + +static unsigned long StrLen(const char *s) { + unsigned long n = 0; + while (*s++) ++n; + return n; +} + +static const char *MemChr(const char *s, unsigned char c, unsigned long n) { + for (; n; --n, ++s) { + if ((*s & 255) == c) { + return s; + } + } + return 0; +} + +static char *GetEnv(char **p, const char *s) { + unsigned long i, j; + if (p) { + for (i = 0; p[i]; ++i) { + for (j = 0;; ++j) { + if (!s[j]) { + if (p[i][j] == '=') { + return p[i] + j + 1; + } + break; + } + if (s[j] != p[i][j]) { + break; + } + } + } + } + return 0; +} + +static char *Utoa(char p[21], unsigned long x) { + char t; + unsigned long i, a, b; + i = 0; + do { + p[i++] = x % 10 + '0'; + x = x / 10; + } while (x > 0); + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} + +static char *Itoa(char p[21], long x) { + if (x < 0) *p++ = '-', x = -(unsigned long)x; + return Utoa(p, x); +} + +#if TROUBLESHOOT +const char *DescribeOs(int os) { + if (os == LINUX) { + return "GNU/SYSTEMD"; + } else if (os == XNU) { + return "XNU"; + } else if (os == FREEBSD) { + return "FREEBSD"; + } else if (os == OPENBSD) { + return "OPENBSD"; + } else if (os == NETBSD) { + return "NETBSD"; + } else { + return "WUT"; + } +} +#endif + +__attribute__((__noreturn__)) static void Exit(long rc, int os) { + asm volatile("call\t*%2" + : /* no outputs */ + : "a"((os == LINUX ? 60 : 1) | (os == XNU ? 0x2000000 : 0)), + "D"(rc), "m"(syscall) + : "memory"); + __builtin_unreachable(); +} + +static void Close(long fd, int os) { long ax, di; - asm volatile("syscall" + asm volatile("call\t*%4" : "=a"(ax), "=D"(di) - : "0"(__NR_close), "1"(fd) + : "0"((os == LINUX ? 3 : 6) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "m"(syscall) : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Read(int os, long fd, void *data, unsigned long size) { - bool cf; +static long Read(long fd, void *data, unsigned long size, int os) { long ax, di, si, dx; - asm volatile("clc\n\t" - "syscall" - : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "1"(__NR_read), "2"(fd), "3"(data), "4"(size) + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 0 : 3) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "2"(data), "3"(size), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static void Write(int os, long fd, const void *data, unsigned long size) { +static void Write(long fd, const void *data, unsigned long size, int os) { long ax, di, si, dx; - asm volatile("syscall" + asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"(__NR_write), "1"(fd), "2"(data), "3"(size) + : "0"((os == LINUX ? 1 : 4) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "2"(data), "3"(size), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Fstat(int os, long fd, union metastat *st) { - long ax, di, si; - asm volatile("syscall" - : "=a"(ax), "=D"(di), "=S"(si) - : "0"(__NR_fstat), "1"(fd), "2"(st) - : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); +static void Execve(const char *prog, char **argv, char **envp, int os) { + long ax, di, si, dx; + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((59) | (os == XNU ? 0x2000000 : 0)), "1"(prog), "2"(argv), + "3"(envp), "m"(syscall) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); +} + +static long Access(const char *path, int mode, int os) { + long ax, dx, di, si; + asm volatile("call\t*%7" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 21 : 33) | (os == XNU ? 0x2000000 : 0)), + "1"(path), "2"(mode), "m"(syscall) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } -static void Msyscall(int os, long p, long n) { +static void Msyscall(long p, long n, int os) { long ax, di, si; if (os == OPENBSD) { - asm volatile("syscall" + asm volatile("call\t*%6" : "=a"(ax), "=D"(di), "=S"(si) - : "0"(37), "1"(p), "2"(n) + : "0"(37), "1"(p), "2"(n), "m"(syscall) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); } } -static long Open(int os, const char *path, long flags, long mode) { - bool cf; +static long Open(const char *path, long flags, long mode, int os) { long ax, di, si, dx; - asm volatile("clc\n\t" - "syscall" - : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "1"(__NR_open), "2"(path), "3"(flags), "4"(mode) + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 2 : 5) | (os == XNU ? 0x2000000 : 0)), + "1"(path), "2"(flags), "3"(mode), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static long Mmap(int os, long addr, long size, long prot, long flags, long fd, - long off) { - bool cf; +__attribute__((__noinline__)) long Mmap(long addr, long size, long prot, + long flags, long fd, long off, int os) { long ax; register long flags_ asm("r10") = flags; register long fd_ asm("r8") = fd; register long off_ asm("r9") = off; asm volatile("push\t%%r9\n\t" - "push\t%%r9\n\t" - "clc\n\t" - "syscall\n\t" - "pop\t%%r9\n\t" + "call\t*%8\n\t" "pop\t%%r9" - : "=@ccc"(cf), "=a"(ax) - : "1"(__NR_mmap), "D"(addr), "S"(size), "d"(prot), "r"(flags_), - "r"(fd_), "r"(off_) + : "=a"(ax) + : "0"((os == LINUX ? 9 + : os == FREEBSD ? 477 + : 197) | + (os == XNU ? 0x2000000 : 0)), + "D"(addr), "S"(size), "d"(prot), "r"(flags_), "r"(fd_), + "r"(off_), "m"(syscall) : "rcx", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static size_t GetFdSize(int os, int fd) { - union metastat st; - if (!Fstat(os, fd, &st)) { - if (os == LINUX) { - return st.linux.st_size; - } else if (os == FREEBSD) { - return st.freebsd.st_size; - } else if (os == OPENBSD) { - return st.openbsd.st_size; - } else { - return st.netbsd.st_size; +static void Emit(int os, const char *s) { + Write(2, s, StrLen(s), os); +} + +static void Perror(int os, const char *c, int rc, const char *s) { + char ibuf[21]; + Emit(os, "ape error: "); + Emit(os, c); + Emit(os, ": "); + Emit(os, s); + if (rc) { + Emit(os, " failed errno="); + Itoa(ibuf, -rc); + Emit(os, ibuf); + } + Emit(os, "\n"); +} + +__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc, + const char *s) { + Perror(os, c, rc, s); + Exit(127, os); +} + +static int StrCmp(const char *l, const char *r) { + unsigned long i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 255) - (r[i] & 255); +} + +static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) { + unsigned long i, m; + if (n >= (m = StrLen(s))) { + for (i = n - m; i < n; ++i) { + if (ToLower(p[i]) != *s++) { + return 0; + } } + return 1; } else { return 0; } } -static size_t Length(const char *s) { - size_t n = 0; - while (*s++) ++n; - return n; +static char IsComPath(struct PathSearcher *ps) { + return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") || + EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") || + EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg"); } -static void Emit(int os, const char *s) { - Write(os, 2, s, Length(s)); +static char AccessCommand(struct PathSearcher *ps, const char *suffix, + unsigned long pathlen) { + unsigned long suffixlen; + suffixlen = StrLen(suffix); + if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0; + if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; + MemCpy(ps->path + pathlen, ps->name, ps->namelen); + MemCpy(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1); + return !Access(ps->path, X_OK, ps->os); } -static void Log(int os, const char *s) { -#ifndef NDEBUG - Emit(os, "ape loader error: "); - Emit(os, s); -#endif -} - -static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) { - size_t i; - int prot, flags; - long code, codesize; - struct Elf64_Phdr *p; - if (e->e_ident[EI_CLASS] != ELFCLASS64) { - Log(os, "EI_CLASS != ELFCLASS64\n"); - return; +static char SearchPath(struct PathSearcher *ps, const char *suffix) { + const char *p; + unsigned long i; + for (p = ps->syspath;;) { + for (i = 0; p[i] && p[i] != ':'; ++i) { + if (i < sizeof(ps->path)) { + ps->path[i] = p[i]; + } + } + if (AccessCommand(ps, suffix, i)) { + return 1; + } else if (p[i] == ':') { + p += i + 1; + } else { + return 0; + } } - if (e->e_ident[EI_DATA] != ELFDATA2LSB) { - Log(os, "EI_CLASS != ELFCLASS64\n"); - return; +} + +static char FindCommand(struct PathSearcher *ps, const char *suffix) { + if (MemChr(ps->name, '/', ps->namelen) || + MemChr(ps->name, '\\', ps->namelen)) { + ps->path[0] = 0; + return AccessCommand(ps, suffix, 0); + } else { + if (AccessCommand(ps, suffix, 0)) return 1; + } + return SearchPath(ps, suffix); +} + +static char *Commandv(struct PathSearcher *ps, int os, const char *name, + const char *syspath) { + ps->os = os; + ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin"; + if (!(ps->namelen = StrLen((ps->name = name)))) return 0; + if (ps->namelen + 1 > sizeof(ps->path)) return 0; + if (FindCommand(ps, "") || (!IsComPath(ps) && FindCommand(ps, ".com"))) { + return ps->path; + } else { + return 0; + } +} + +__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, + long *sp, char *page, + struct ElfEhdr *e) { + long rc; + unsigned long i; + int prot, flags; + struct ElfPhdr *p; + long code, codesize; + if (e->e_type != ET_EXEC) { + Pexit(os, exe, 0, "ELF e_type != ET_EXEC"); } if (e->e_machine != EM_NEXGEN32E) { - Log(os, "e_machine != EM_NEXGEN32E\n"); - return; + Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E"); } - if (e->e_type != ET_EXEC) { - Log(os, "e_type != ET_EXEC\n"); - return; + if (e->e_ident[EI_CLASS] != ELFCLASS64) { + Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64"); + } + if (e->e_ident[EI_DATA] != ELFDATA2LSB) { + Pexit(os, exe, 0, "ELF e_ident[EI_DATA] != ELFDATA2LSB"); } if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) { - Log(os, "phnum out of bounds\n"); - return; + Pexit(os, exe, 0, "ELF phdrs need to be in first page"); } code = 0; codesize = 0; - for (p = (struct Elf64_Phdr *)(b + e->e_phoff), i = e->e_phnum; i--;) { + for (p = (struct ElfPhdr *)(page + e->e_phoff), i = e->e_phnum; i--;) { + if (p[i].p_type == PT_DYNAMIC) { + Pexit(os, exe, 0, "not a real executable"); + } if (p[i].p_type != PT_LOAD) continue; if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) { - Log(os, "ape requires strict page size padding and alignment\n"); - return; + Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded"); } prot = 0; flags = MAP_FIXED | MAP_PRIVATE; @@ -228,107 +498,189 @@ static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) { codesize = p[i].p_filesz; } if (p[i].p_memsz > p[i].p_filesz) { - if (Mmap(os, p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz, - prot, flags | MAP_ANONYMOUS, -1, 0) < 0) { - Log(os, "bss mmap failed\n"); - return; + if ((rc = Mmap(p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz, + prot, flags | MAP_ANONYMOUS, -1, 0, os)) < 0) { + Pexit(os, exe, rc, "bss mmap()"); } } if (p[i].p_filesz) { - if (Mmap(os, p[i].p_vaddr, p[i].p_filesz, prot, flags, fd, - p[i].p_offset) < 0) { - Log(os, "image mmap failed\n"); - return; + if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd, + p[i].p_offset, os)) < 0) { + Pexit(os, exe, rc, "image mmap()"); } } } - Close(os, fd); - Msyscall(os, code, codesize); - sp[1] = sp[0] - 1; - ++sp; - asm volatile("mov\t%2,%%rsp\n\t" - "jmpq\t*%1" + if (!code) { + Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X"); + } + Close(fd, os); + Msyscall(code, codesize, os); +#if TROUBLESHOOT + Emit(TROUBLESHOOT_OS, "preparing to jump\n"); +#endif + register long r8 asm("r8") = syscall; + asm volatile("xor\t%%eax,%%eax\n\t" + "xor\t%%ebx,%%ebx\n\t" + "xor\t%%r9d,%%r9d\n\t" + "xor\t%%r10d,%%r10d\n\t" + "xor\t%%r11d,%%r11d\n\t" + "xor\t%%r12d,%%r12d\n\t" + "xor\t%%r13d,%%r13d\n\t" + "xor\t%%r14d,%%r14d\n\t" + "xor\t%%r15d,%%r15d\n\t" + "mov\t%%rdx,%%rsp\n\t" + "xor\t%%edx,%%edx\n\t" + "push\t%%rsi\n\t" + "xor\t%%esi,%%esi\n\t" + "xor\t%%ebp,%%ebp\n\t" + "ret" : /* no outputs */ - : "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp) + : "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os), + "r"(r8) : "memory"); - unreachable; + __builtin_unreachable(); } -void loader(long di, long *sp) { - size_t size; +__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, + struct ApeLoader *handoff) { long rc, *auxv; - char *p, **argv; + struct ElfEhdr *ehdr; int c, i, fd, os, argc; - union { - struct Elf64_Ehdr ehdr; + char *p, *exe, *prog, **argv, **envp, *page; + static union { + struct ElfEhdr ehdr; char p[0x1000]; } u; - os = 0; + + // detect freebsd if (di) { os = FREEBSD; sp = (long *)di; + } else if (dl == XNU) { + os = XNU; + } else { + os = LINUX; } + + // extract arguments argc = *sp; argv = (char **)(sp + 1); - auxv = (long *)(argv + argc + 1); + envp = (char **)(sp + 1 + argc + 1); + auxv = (long *)(sp + 1 + argc + 1); for (;;) { if (!*auxv++) { break; } } - if (!auxv[0]) { - os = OPENBSD; + + // get syscall function pointer + if (handoff && handoff->syscall) { + syscall = handoff->syscall; + } else { + syscall = __syscall_loader; } - for (; auxv[0]; auxv += 2) { - if (!os) { - if (auxv[0] == AT_EXECFN_NETBSD) { - os = NETBSD; - if (argc > 1) { - auxv[1] = (long)argv[1]; - } - } else if (auxv[0] == AT_EXECFN_LINUX) { - if (argc > 1) { - auxv[1] = (long)argv[1]; + + if (handoff) { + // we were called by ape_execve() + // no argument parsing is needed + // no path searching is needed + exe = handoff->prog; + fd = handoff->fd; + os = handoff->os; + exe = handoff->prog; + page = handoff->page; + ehdr = (struct ElfEhdr *)handoff->page; + } else { + + // detect openbsd + if (!auxv[0]) { + os = OPENBSD; + } + + // detect netbsd + if (os == LINUX) { + for (; auxv[0]; auxv += 2) { + if (auxv[0] == AT_EXECFN_NETBSD) { + os = NETBSD; + break; } } } + + // we can load via shell, shebang, or binfmt_misc + if (argc >= 3 && !StrCmp(argv[1], "-")) { + // if the first argument is a hyphen then we give the user the + // power to change argv[0] or omit it entirely. most operating + // systems don't permit the omission of argv[0] but we do, b/c + // it's specified by ANSI X3.159-1988. + prog = (char *)sp[3]; + argc = sp[3] = sp[0] - 3; + argv = (char **)((sp += 3) + 1); + } else if (argc < 2) { + Emit(os, "usage: ape PROG [ARGV1,ARGV2,...]\n" + " ape - PROG [ARGV0,ARGV1,...]\n" + "αcτµαlly pδrταblε εxεcµταblε loader v1.o\n" + "copyright 2022 justine alexandra roberts tunney\n" + "https://justine.lol/ape.html\n"); + Exit(1, os); + } else { + prog = (char *)sp[2]; + argc = sp[1] = sp[0] - 1; + argv = (char **)((sp += 1) + 1); + } + + if (!(exe = Commandv(&ps, os, prog, GetEnv(envp, "PATH")))) { + Pexit(os, prog, 0, "not found (maybe chmod +x)"); + } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { + Pexit(os, exe, fd, "open"); + } else if ((rc = Read(fd, u.p, sizeof(u.p), os)) < 0) { + Pexit(os, exe, rc, "read"); + } else if (rc != sizeof(u.p) && Read32(u.p) != Read32("\177ELF")) { + Pexit(os, exe, 0, "too small"); + } + + page = u.p; + ehdr = &u.ehdr; } - if (argc < 2) { - Emit(os, "usage: loader PROG [ARGS...]\n"); - } else if ((fd = Open(os, argv[1], O_RDONLY, 0)) < 0) { - Log(os, "open failed\n"); - } else if ((rc = Read(os, fd, u.p, sizeof(u.p))) < 0) { - Log(os, "read failed\n"); - } else if (rc != sizeof(u.p)) { - Log(os, "file too small\n"); - } else if (READ32LE(u.p) == READ32LE("\177ELF")) { - Spawn(os, fd, sp, u.p, &u.ehdr); - } else { - for (p = u.p; p < u.p + sizeof(u.p); ++p) { - if (READ64LE(p) == READ64LE("printf '")) { - for (i = 0, p += 8; p + 3 < u.p + sizeof(u.p) && (c = *p++) != '\'';) { - if (c == '\\') { + +#if TROUBLESHOOT + Emit(TROUBLESHOOT_OS, "os = "); + Emit(TROUBLESHOOT_OS, DescribeOs(os)); + Emit(TROUBLESHOOT_OS, "\n"); + for (i = 0; i < argc; ++i) { + Emit(TROUBLESHOOT_OS, "argv = "); + Emit(TROUBLESHOOT_OS, argv[i]); + Emit(TROUBLESHOOT_OS, "\n"); + } +#endif + + if (Read32(page) == Read32("\177ELF") || Read32(page) == 0xFEEDFACE + 1) { + Close(fd, os); + Execve(exe, argv, envp, os); + } + + for (p = page; p < page + sizeof(u.p); ++p) { + if (Read64(p) != Read64("printf '")) continue; + for (i = 0, p += 8; p + 3 < page + sizeof(u.p) && (c = *p++) != '\'';) { + if (c == '\\') { + if ('0' <= *p && *p <= '7') { + c = *p++ - '0'; + if ('0' <= *p && *p <= '7') { + c *= 8; + c += *p++ - '0'; if ('0' <= *p && *p <= '7') { - c = *p++ - '0'; - if ('0' <= *p && *p <= '7') { - c *= 8; - c += *p++ - '0'; - if ('0' <= *p && *p <= '7') { - c *= 8; - c += *p++ - '0'; - } - } + c *= 8; + c += *p++ - '0'; } } - u.p[i++] = c; - } - if (i >= 64 && READ32LE(u.p) == READ32LE("\177ELF")) { - Spawn(os, fd, sp, u.p, &u.ehdr); - Exit(os, 127); } } + page[i++] = c; + } + if (i >= 64 && Read32(page) == Read32("\177ELF")) { + Spawn(os, exe, fd, sp, page, ehdr); } - Log(os, "could not find printf elf in first page\n"); } - Exit(os, 127); + + Pexit(os, exe, 0, "could not find printf elf in first page"); } diff --git a/ape/loader.h b/ape/loader.h new file mode 100644 index 000000000..d36d7a567 --- /dev/null +++ b/ape/loader.h @@ -0,0 +1,21 @@ +#ifndef COSMOPOLITAN_APE_LOADER_H_ +#define COSMOPOLITAN_APE_LOADER_H_ + +#define APE_LOADER_BASE 0x200000 +#define APE_LOADER_SIZE 0x200000 +#define APE_LOADER_BSS (PAGESIZE * 2) +#define APE_LOADER_STACK 0x7f0000000000 +#define APE_LOADER_ENTRY (APE_LOADER_BASE + 0x47) +#define APE_LOADER_SYSCALL (APE_LOADER_BASE + 0x50) +#define APE_BLOCK_BASE 0x7e0000000000 +#define APE_BLOCK_SIZE 0x000200000000 + +struct ApeLoader { + int fd; + int os; + char *prog; + char *page; + void *syscall; +}; + +#endif /* COSMOPOLITAN_APE_LOADER_H_ */ diff --git a/ape/loader.lds b/ape/loader.lds index 403ae5f9f..e380bdb1b 100644 --- a/ape/loader.lds +++ b/ape/loader.lds @@ -24,9 +24,18 @@ SECTIONS { .text : { *(.text) *(.rodata .rodata.*) + . = ALIGN(4096); } filesz = . - ehdr; + .bss ALIGN(4096) : { + bss = .; + *(.bss) + . = ALIGN(4096); + } + memsz = . - ehdr; /DISCARD/ : { *(.*) } } + +bsssize = SIZEOF(.bss); diff --git a/build/bootstrap/ape b/build/bootstrap/ape new file mode 100644 index 0000000000000000000000000000000000000000..d8857f6f44bdfd26b55648507883c54887dfa155 GIT binary patch literal 4096 zcmeGe{cjWJ`OdK$CnTKtFdr?Hm&#Ge+9X_-0L~Kdfe&6!X9&dsI-sdzJCMz>Q~OMa zMw_5Zy1Y5ALe(bqhoMQGv_hp#u$48iH8_qMNIybIJBmPUH?_109t|IA2r#%k@5NaM z-TnhC``+j4eZHUP+$u_IO)Q(kWIiTlCBp)|jzI?HdEm z!)=vw7}Xcv4Q@<9!`y9oy_VCA1^ zBGwHoaWE^%}$0-V&~QPoUYld ztE-ZCbyl)iPj=4cu|Al-gf-bQ4P3B;K{5sou;9Q*HemXPCuokkE8)S3sgtcnxCa+A zpwQiAHi_*?nv-PuI(GKQ$}qWx3wv?lRm=`y=e0QEdS-KO?Z+nt<<|V>EG329Tu+`= zaGp(AvEG}$C>&wkB-v*u%l=N8q~oCET7v1@@D_ry^d>KH&SA%Kcohf-b_vbR2XXRc=Vk|ui%H$l93xGHS+DN&#Ju$WolQDjLhp=7P z(QgsqHX}SY`prFE=f{CF@+Cm+$;1XpEm#apNh^AS8oMZTnHPAqvIUU+xs?_2EWGcI zp2aZD7NOI<5aR1ge~QUxRKx3&8uEp>Wy18*Dy;t8 z#t%!Hr53hVSKeHqtIXzFaV?S4J|O4P(M`Kn3?^1%Wh6^d1M|RBgq6N|m}HH<3Hg(k z7~SuH%Og4e`hNG*ZqV(&d7QS;X;1*Mt+8TljZGpqJfwfL9(3Tq1fNvyvM1t{ovVSAJH~>AR49vttazIJlj(O?1p!5%*baX%H)KAL=9(~ZGTv_4m80Xs4a2JYn z8d@qdFZf&mcpsNVA)^~Dj73hfO+=S<+7tDb4%&|#f}?#ct%SE=lB>bfd^Ado(x zH*FG>FW5sS*(@mUn+~yZJ|=k%fX$ffg~@T=jIVN&?G6VnyocE{p1?=2X(Y!ykst6V zr}dEvXw+xv7{gd4F-f(|05d-UGZLY%mHJz{4*rb5lyU9Lpr^g6cLaO9=UtzrX;ZX;54l)2uR<#z|0;?=UE*J8{uCB7VuJ-Xnep{ujDlrplZX27P z;i@RJC5#t0tf^jW`i!fy=r42NUPm&KjWx?0sD?xex+#fRj>7@lysgp;V(3Ro%`#3jZXaj<8P1 zOSFnwK5a&l8odp9+gJ5I>>4_1K(7Hh*Zvv^js6K<4F0AN3dLg4SOsF>i|YLi;ZTs0 zBGD=)mno@hh*Xp$;<1u&G~f?^hXS1`ViIxx&QJxSS`ey!X2UwPO?YPAX8vjVEPcAT zxOhh{aOj816#bXXQbUHHlo>o4IMbdwHh44~Zb40hZ^h2E4<4-x4<18<$My^!3#5+y zhY*hXgP|DO%@;>=1JS0ISi{a;GAdnKT8e&}h|3L;5b}pZd;F1L%#UKxx=>7xBRLU? zgj#ZU$#PS?qNF4vSR9UqOTe$rmn(4LS16$k5fGEw;J$izbaAR|YkDBkD~hZ#sT3f0Oj5b)wEB>HN_t>T&t zwb38gMO~x}Z|!<<>pF4$4@<=iw;>pc$lF9u?HWngv}uPSC9O4(Wn~Y_xCNguB|_m+ zY13*$rfF9&7Dtf~)KQKi2%#Z@>KkHl88tz*j0hn!A%7S_D1n6RuM3A5p;|PA$h#wXas%Ah->s ysJR4HudUt61fq#>5E(gc0F9ZKQz%^jfRFXX+@f~6Gnj|a!yG*9gYV74fqw%EhS9A6 literal 0 HcmV?d00001 diff --git a/build/sanitycheck b/build/sanitycheck index c2da2d263..7a06eb647 100755 --- a/build/sanitycheck +++ b/build/sanitycheck @@ -37,15 +37,14 @@ ERROR DETAILS - Actually Portable Executable assumes stock Linux configuration. - Normal behavior is non-ELF files with x bit are run by /bin/sh. - Linux lets people globally define arbitrary magic interpreters. - Your computer couldve been tuned to run MZ scripts inside WINE. - So if you use binfmt_misc you need to explicitly register this. + Your system has likely been configured to use binfmt_misc and wine. + You need to run the command below which will install a /usr/bin/ape + program and then register it with binfmt_misc. See ape/loader.c for + source code and technical details. WORKAROUND - sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register" + ape/apeinstall.sh SEE ALSO diff --git a/build/sanitycheck2 b/build/sanitycheck2 index 152c3644a..4e74e3964 100755 --- a/build/sanitycheck2 +++ b/build/sanitycheck2 @@ -1,5 +1,12 @@ -MZqFpD=123 -exit $MZqFpD +MZboop=123 +exit $MZboop + + + + + + + diff --git a/examples/examples.mk b/examples/examples.mk index 127f9e2f5..388b019b3 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -104,6 +104,14 @@ o/$(MODE)/examples/%.com.dbg: \ $(APE) @$(APELINK) +o/$(MODE)/examples/nomodifyself.com.dbg: \ + $(EXAMPLES_DEPS) \ + o/$(MODE)/examples/nomodifyself.o \ + o/$(MODE)/examples/examples.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + o/$(MODE)/examples/hellolua.com.dbg: \ $(EXAMPLES_DEPS) \ o/$(MODE)/examples/hellolua.o \ diff --git a/examples/mkhello.c b/examples/mkhello.c new file mode 100644 index 000000000..c5448ae1a --- /dev/null +++ b/examples/mkhello.c @@ -0,0 +1,17 @@ +#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" + +int main(int argc, char *argv[]) { + creat("hello.txt", 0644); + write(3, "hello\n", 6); + close(3); + return 0; +} diff --git a/examples/nomodifyself.c b/examples/nomodifyself.c new file mode 100644 index 000000000..7db1e89e6 --- /dev/null +++ b/examples/nomodifyself.c @@ -0,0 +1,36 @@ +#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/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" + +/** + * @fileoverview Non-Self-Modifying APE Binary Demo + * + * See examples/examples.mk for the build config, which uses the + * alternative APE runtime. + */ + +int main(int argc, char *argv[]) { + if (_base[0] == 'M' && _base[1] == 'Z') { + printf("success: %s spawned without needing to modify its " + "executable header, thanks to APE loader\n", + argv[0]); + if (!IsWindows()) { + printf(", thanks to APE loader!\n"); + } else { + printf(", because you ran it on Windows :P\n"); + } + return 0; + } else { + printf("error: %s doesn't have an MZ file header!\n", argv[0]); + return 1; + } +} diff --git a/libc/calls/mman.greg.c b/libc/calls/mman.greg.c index bc50271a4..89df1691a 100644 --- a/libc/calls/mman.greg.c +++ b/libc/calls/mman.greg.c @@ -64,7 +64,7 @@ noasan texthead uint64_t __new_page(struct mman *mm) { * Returns pointer to page table entry for page at virtual address. * Additional page tables are allocated if needed as a side-effect. */ -noasan texthead uint64_t *__get_virtual(struct mman *mm, uint64_t *t, +noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t, int64_t vaddr, bool maketables) { uint64_t *e, p; unsigned char h; diff --git a/libc/calls/nanos.c b/libc/calls/nanos.c new file mode 100644 index 000000000..14728f272 --- /dev/null +++ b/libc/calls/nanos.c @@ -0,0 +1,34 @@ +/*-*- 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/timespec.h" +#include "libc/time/time.h" + +/** + * Returns nanoseconds since UNIX epoch. + */ +int128_t _nanos(int timer) { + int128_t nanos; + struct timespec ts; + clock_gettime(timer, &ts); + nanos = ts.tv_sec; + nanos *= 1000000000; + nanos += ts.tv_nsec; + return nanos; +} diff --git a/libc/calls/nanos.h b/libc/calls/nanos.h new file mode 100644 index 000000000..547557290 --- /dev/null +++ b/libc/calls/nanos.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_NANOS_H_ +#define COSMOPOLITAN_LIBC_CALLS_NANOS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int128_t _nanos(int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_NANOS_H_ */ diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index 7414fd999..d94d02e18 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -21,6 +21,7 @@ #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/intrin/cmpxchg.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" @@ -131,9 +132,7 @@ static privileged bool __sig_deliver(bool restartable, int sig, int si_code, // since sigaction() is @asyncsignalsafe we only restore it if the // user didn't change it during the signal handler. we also don't // need to do anything if this was a oneshot signal or nodefer. - _spinlock(&__sig_lock); - _cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva); - _spunlock(&__sig_lock); + _lockcmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva); } if (!restartable) { diff --git a/libc/crt/crt.S b/libc/crt/crt.S index 83a86ab20..f30fde679 100644 --- a/libc/crt/crt.S +++ b/libc/crt/crt.S @@ -26,6 +26,8 @@ // // @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..] // @note FreeBSD is special (see freebsd/lib/csu/amd64/...) +// @note NetBSD will only zero the call-clobbered registers +// @note ape.S and ape-loader both set RCX to XNU on Darwin // @noreturn _start: @@ -34,12 +36,16 @@ _start: test %rdi,%rdi cmovnz %rdi,%rsp jz 0f - movb $FREEBSD,__hostos(%rip) + movb $FREEBSD,%cl +0: #endif +// set operating system when already detected + mov %cl,__hostos(%rip) + // get startup timestamp as early as possible // its used by --strace flag and kprintf() %T -0: rdtsc + rdtsc ezlea kStartTsc,bx mov %eax,(%rbx) mov %edx,4(%rbx) @@ -85,14 +91,3 @@ _start: call cosmo 9: .unreachable .endfn _start,weak,hidden - -#if SupportsXnu() -// Macintosh userspace program entrypoint. -// -// @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..] -// @note FreeBSD is special (see freebsd/lib/csu/amd64/...) -// @noreturn -_xnu: movb $XNU,__hostos(%rip) - jmp 0b - .endfn _xnu,weak,hidden -#endif diff --git a/libc/fmt/leb128.h b/libc/fmt/leb128.h index 25b92c4f9..a16f2e960 100644 --- a/libc/fmt/leb128.h +++ b/libc/fmt/leb128.h @@ -4,7 +4,7 @@ COSMOPOLITAN_C_START_ char *sleb64(char *, int64_t); -char *zleb64(char *, int64_t); +char *zleb64(char[hasatleast 10], int64_t); char *uleb64(char[hasatleast 10], uint64_t); int unzleb64(const char *, size_t, int64_t *); int unuleb64(char *, size_t, uint64_t *); diff --git a/libc/fmt/zleb64.c b/libc/fmt/zleb64.c index 1ca96cd52..48a3f5c58 100644 --- a/libc/fmt/zleb64.c +++ b/libc/fmt/zleb64.c @@ -44,7 +44,7 @@ * @return p + i * @see unzleb64() */ -char *zleb64(char *p, int64_t x) { +char *zleb64(char p[hasatleast 10], int64_t x) { int c; uint64_t u; u = x; diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 5a7b87b37..779a12bf7 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -58,7 +58,10 @@ struct CloneArgs { uint32_t utid; int64_t tid64; }; - int lock; + union { + int lock; + void *pstack; + }; int *ctid; int *ztid; char *tls; @@ -287,12 +290,18 @@ __attribute__((__used__, __no_reorder__)) static privileged wontreturn void OpenbsdThreadMain(struct CloneArgs *wt) { wt->func(wt->arg); - // we no longer use the stack after this point + // we no longer use the stack after this point. however openbsd + // validates the rsp register too so a race condition can still + // happen if the parent tries to free the stack. we'll solve it + // by simply changing rsp back to the old value before exiting! + // although ideally there should be a better solution. + // // void __threxit(%rdi = int32_t *notdead); - asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 + asm volatile("mov\t%3,%%rsp\n\t" + "movl\t$0,%0\n\t" // *wt->ztid = 0 "syscall" // _Exit1() : "=m"(*wt->ztid) - : "a"(302), "D"(0) + : "a"(302), "D"(0), "r"(wt->pstack) : "rcx", "r11", "memory"); unreachable; } @@ -307,6 +316,7 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, -alignof(struct CloneArgs)); wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; + wt->pstack = __builtin_frame_address(0); wt->func = func; wt->arg = arg; params.tf_stack = wt; diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 58deed8bb..ae0359265 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -47,7 +47,7 @@ static struct Ftrace { int64_t lastaddr; } g_ftrace; -static privileged int GetNestingLevelImpl(struct StackFrame *frame) { +static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) { int nesting = -2; while (frame) { ++nesting; @@ -56,7 +56,7 @@ static privileged int GetNestingLevelImpl(struct StackFrame *frame) { return MAX(0, nesting); } -static privileged int GetNestingLevel(struct StackFrame *frame) { +static privileged inline int GetNestingLevel(struct StackFrame *frame) { int nesting; nesting = GetNestingLevelImpl(frame); if (nesting < g_ftrace.skew) g_ftrace.skew = nesting; diff --git a/libc/testlib/ugly.h b/libc/testlib/ugly.h index e8c58e59f..c08baee03 100644 --- a/libc/testlib/ugly.h +++ b/libc/testlib/ugly.h @@ -9,9 +9,9 @@ #define __BENCH_ARRAY(S) \ _Section(".piro.relo.sort.bench.2." #S ",\"aw\",@init_array #") -#define __TEST_PROTOTYPE(S, N, A, K) \ - void S##_##N(void); \ - const void *const S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \ +#define __TEST_PROTOTYPE(S, N, A, K) \ + void S##_##N(void); \ + testfn_t S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \ testonly K void S##_##N(void) #define __TEST_SECTION(NAME, CONTENT) \ diff --git a/test/libc/calls/execve_test.c b/test/libc/calls/execve_test.c index 88cbd140f..2dd8ae76e 100644 --- a/test/libc/calls/execve_test.c +++ b/test/libc/calls/execve_test.c @@ -23,13 +23,7 @@ #include "libc/testlib/testlib.h" void SetUp(void) { - if (getenv("_SUBPROCESS")) { - if (!__argv[0]) { - exit(0); - } else { - exit(7); - } - } else if (getenv("_WEIRDENV")) { + if (getenv("_WEIRDENV")) { for (char **e = environ; *e; ++e) { if (!strcmp(*e, "WEIRD")) { exit(0); @@ -39,22 +33,6 @@ void SetUp(void) { } } -TEST(execve, testWeirdAnsiC89emptyArgv) { - char *prog; - int pid, ws; - if (IsWindows()) return; - if (IsOpenbsd()) return; - prog = GetProgramExecutableName(); - ASSERT_NE(-1, (pid = fork())); - if (!pid) { - execve(prog, (char *const[]){0}, (char *const[]){"_SUBPROCESS=1", 0}); - _Exit(127); - } - ASSERT_NE(-1, wait(&ws)); - EXPECT_TRUE(WIFEXITED(ws)); - EXPECT_EQ(0, WEXITSTATUS(ws)); -} - TEST(execve, testWeirdEnvironmentVariable) { char *prog; int pid, ws; diff --git a/test/libc/str/test.mk b/test/libc/str/test.mk index 6efecbbf3..7b3ec9f89 100644 --- a/test/libc/str/test.mk +++ b/test/libc/str/test.mk @@ -3,24 +3,32 @@ PKGS += TEST_LIBC_STR -TEST_LIBC_STR_SRCS := $(wildcard test/libc/str/*.c) -TEST_LIBC_STR_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_STR_SRCS)) +TEST_LIBC_STR_FILES := $(wildcard test/libc/str/*) +TEST_LIBC_STR_SRCS_C = $(filter %.c,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS_CC = $(filter %.cc,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS = $(TEST_LIBC_STR_SRCS_C) $(TEST_LIBC_STR_SRCS_CC) +TEST_LIBC_STR_SRCS_TEST_C = $(filter %_test.c,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS_TEST_CC = $(filter %_test.cc,$(TEST_LIBC_STR_FILES)) TEST_LIBC_STR_OBJS = \ - $(TEST_LIBC_STR_SRCS:%.c=o/$(MODE)/%.o) + $(TEST_LIBC_STR_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(TEST_LIBC_STR_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_STR_COMS = \ - $(TEST_LIBC_STR_SRCS:%.c=o/$(MODE)/%.com) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com) TEST_LIBC_STR_BINS = \ $(TEST_LIBC_STR_COMS) \ $(TEST_LIBC_STR_COMS:%=%.dbg) TEST_LIBC_STR_TESTS = \ - $(TEST_LIBC_STR_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com.ok) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com.ok) TEST_LIBC_STR_CHECKS = \ - $(TEST_LIBC_STR_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com.runs) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com.runs) TEST_LIBC_STR_DIRECTDEPS = \ LIBC_ALG \ @@ -43,7 +51,9 @@ TEST_LIBC_STR_DIRECTDEPS = \ LIBC_ZIPOS \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_REGEX \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ + THIRD_PARTY_LIBCXX \ + THIRD_PARTY_SMALLZ4 TEST_LIBC_STR_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_STR_DIRECTDEPS),$($(x)))) diff --git a/third_party/make/config.h b/third_party/make/config.h index 48f976078..14385ed0b 100644 --- a/third_party/make/config.h +++ b/third_party/make/config.h @@ -620,7 +620,7 @@ /* #undef HAVE__SET_INVALID_PARAMETER_HANDLER */ /* Build host information. */ -#define MAKE_HOST "x86_64-pc-linux-gnu" +#define MAKE_HOST "x86_64-pc-cosmopolitan" /* Define to 1 to enable job server support in GNU make. */ /* TODO(jart): make it work */ diff --git a/third_party/make/dir.c b/third_party/make/dir.c index b8b884ba9..9a828158b 100644 --- a/third_party/make/dir.c +++ b/third_party/make/dir.c @@ -689,15 +689,9 @@ void print_dir_data_base(void) { if (dir->contents == 0) printf(_("# %s: could not be stat'd.\n"), dir->name); else if (dir->contents->dirfiles.ht_vec == 0) { -#ifdef WINDOWS32 - printf(_("# %s (key %s, mtime %I64u): could not be opened.\n"), - dir->name, dir->contents->path_key, - (unsigned long long)dir->contents->mtime); -#else /* WINDOWS32 */ printf(_("# %s (device %ld, inode %ld): could not be opened.\n"), dir->name, (long int)dir->contents->dev, (long int)dir->contents->ino); -#endif /* WINDOWS32 */ } else { unsigned int f = 0; unsigned int im = 0; @@ -715,14 +709,8 @@ void print_dir_data_base(void) { ++f; } } -#ifdef WINDOWS32 - printf(_("# %s (key %s, mtime %I64u): "), dir->name, - dir->contents->path_key, - (unsigned long long)dir->contents->mtime); -#else /* WINDOWS32 */ printf(_("# %s (device %ld, inode %ld): "), dir->name, (long)dir->contents->dev, (long)dir->contents->ino); -#endif /* WINDOWS32 */ if (f == 0) fputs(_("No"), stdout); else @@ -822,9 +810,7 @@ static struct dirent *read_dirstream(__ptr_t stream) { #ifdef _DIRENT_HAVE_D_NAMLEN d->d_namlen = len - 1; #endif -#ifdef HAVE_STRUCT_DIRENT_D_TYPE d->d_type = df->type; -#endif memcpy(d->d_name, df->name, len); return d; } diff --git a/third_party/smallz4/smallz4.hh b/third_party/smallz4/smallz4.hh index 310c89831..abf9b5683 100644 --- a/third_party/smallz4/smallz4.hh +++ b/third_party/smallz4/smallz4.hh @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_ #define COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_ +#include "libc/bits/bits.h" #include "third_party/libcxx/vector" /** @@ -138,7 +139,7 @@ class smallz4 { /// return true, if the four bytes at *a and *b match inline static bool match4(const void* const a, const void* const b) { - return *(const uint32_t*)a == *(const uint32_t*)b; + return READ32LE(a) == READ32LE(b); } /// simple hash function, input: 32 bits, output: HashBits bits (by default: @@ -636,7 +637,7 @@ class smallz4 { } // read next four bytes - const uint32_t four = *(uint32_t*)(dataBlock + i); + const uint32_t four = READ32LE(dataBlock + i); // convert to a shorter hash const uint32_t hash = getHash32(four); @@ -674,10 +675,9 @@ class smallz4 { // check the hash chain while (true) { // read four bytes - currentFour = - *(uint32_t*)(&data[lastHashMatch - - dataZero]); // match may be found in the - // previous block, too + currentFour = READ32LE( + &data[lastHashMatch - dataZero]); // match may be found in the + // previous block, too // match chain found, first 4 bytes are identical if (currentFour == four) break; From 056dc5f554823efea19b45ae6b5a4f4b6cd37659 Mon Sep 17 00:00:00 2001 From: ProducerMatt <58014742+ProducerMatt@users.noreply.github.com> Date: Sat, 21 May 2022 22:13:01 -0500 Subject: [PATCH 29/40] Extend quotas for various slow tests (#411) --- third_party/python/python.mk | 12 +++++++----- third_party/quickjs/quickjs.mk | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 34f4d6b8f..412fbae67 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -4191,21 +4191,21 @@ $(THIRD_PARTY_PYTHON_PYTEST_A_PYS_OBJS): PYFLAGS += -P.python -C3 $(THIRD_PARTY_PYTHON_PYTEST_A_DATA_OBJS): ZIPOBJ_FLAGS += -P.python -C3 o/$(MODE)/third_party/python/Python/ceval.o: QUOTA = -C64 -M1024m -o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C64 -M1024m +o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C64 -M1024m -L180 o/$(MODE)/third_party/python/Parser/asdl_c.o: PYFLAGS += -m $(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_OBJS): PYFLAGS += -t -P.python -C3 $(THIRD_PARTY_PYTHON_PYTEST_TODOS:%.py=o/$(MODE)/%.o): PYFLAGS += -t -P.python -C3 o/$(MODE)/third_party/python/Lib/test/pystone.o: PYFLAGS += -m -O2 -P.python -C4 -o/$(MODE)/third_party/python/Lib/test/test_long.py.runs: QUOTA = -C64 +o/$(MODE)/third_party/python/Lib/test/test_long.py.runs: QUOTA = -C64 -L180 o/$(MODE)/third_party/python/Lib/test/test_hash.py.runs: QUOTA = -C64 o/$(MODE)/third_party/python/Lib/test/test_exceptions.py.runs: QUOTA = -C64 o/$(MODE)/third_party/python/Lib/test/test_tuple.py.runs: QUOTA = -M512m -o/$(MODE)/third_party/python/Lib/test/test_decimal.py.runs: QUOTA = -M512m -C64 +o/$(MODE)/third_party/python/Lib/test/test_decimal.py.runs: QUOTA = -M512m -C64 -L300 o/$(MODE)/third_party/python/Lib/test/test_longexp.py.runs: QUOTA = -M1024m -o/$(MODE)/third_party/python/Lib/test/test_unicode.py.runs: QUOTA = -M1400m -o/$(MODE)/third_party/python/Lib/test/test_unicodedata.py.runs: QUOTA = -C64 +o/$(MODE)/third_party/python/Lib/test/test_unicode.py.runs: QUOTA = -M1400m -L300 +o/$(MODE)/third_party/python/Lib/test/test_unicodedata.py.runs: QUOTA = -C64 -L300 o/$(MODE)/third_party/python/Lib/test/test_logging.py.runs: QUOTA = -M512m o/$(MODE)/third_party/python/Lib/test/test_itertools.py.runs: QUOTA = -M1024m o/$(MODE)/third_party/python/Lib/test/test_tarfile.py.runs: QUOTA = -L120 -C64 @@ -4214,6 +4214,8 @@ o/$(MODE)/third_party/python/Lib/test/test_gzip.py.runs: QUOTA = -L120 o/$(MODE)/third_party/python/Lib/test/test_logging.py.runs: QUOTA = -M512m o/$(MODE)/third_party/python/Lib/test/test_resource.py.runs: QUOTA = -C1000000 o/$(MODE)/third_party/python/Lib/test/test_email/test_email.py.runs: QUOTA = -C32 -M1024m +o/$(MODE)/third_party/python/Lib/test/test_selectors.py.runs: QUOTA = -L180 +o/$(MODE)/third_party/python/Lib/test/test_tracemalloc.py.runs: QUOTA = -L300 THIRD_PARTY_PYTHON_LIBS = \ $(foreach x,$(THIRD_PARTY_PYTHON_ARTIFACTS),$($(x))) diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 8088fecce..83c5749aa 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -205,8 +205,8 @@ o/$(MODE)/third_party/quickjs/quickjs.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED -o/$(MODE)/third_party/quickjs/call.o: QUOTA = -M1024m -C32 -o/$(MODE)/third_party/quickjs/quickjs.o: QUOTA = -M512m -C32 +o/$(MODE)/third_party/quickjs/call.o: QUOTA = -M1024m -C32 -L180 +o/$(MODE)/third_party/quickjs/quickjs.o: QUOTA = -M512m -C32 -L180 .PHONY: o/$(MODE)/third_party/quickjs o/$(MODE)/third_party/quickjs: \ From 4e9662cbc78ee4ee30cf660b3590e82293f5d4cc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 22 May 2022 04:51:02 -0700 Subject: [PATCH 30/40] Write tests for new APE loader and fix bugs - Add FreeBSD-specific mmap() flags - Reduce size of the APE loader from 8kb to 4kb - Work towards fixing the Makefile build on WSL - Automate testing of APE no-modify-self behaviors - Make the ape.S shell script code cleaner and tinier - Improve the APE sanity check to test behavior better - Fixed issue with ShowCrashReports() sigaltstack() on BSDs - Delete symbols for S_MODE magnums which wasted compile time If you checked out yesterday's APE commit, please run: rm -f /usr/bin/ape o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape" Because this change fixes certain aspects of the new ABI. We don't have automated migrations for APE loader versions yet. Thanks! You can also download prebuilt binaries here: - https://justine.lol/ape.elf (Linux/FreeBSD/NetBSD/OpenBSD) - https://justine.lol/ape.macho (Apple) Install the appropriate one as `/usr/bin/ape`. --- ape/ape.S | 145 +++++++-------- ape/ape.lds | 37 ++-- ape/ape.mk | 68 +++++-- ape/apeinstall.sh | 12 +- ape/loader-elf.S | 186 ++++++++++++++----- ape/loader-macho.S | 6 +- ape/loader-macho.lds | 1 - ape/loader.c | 194 ++++++++++++-------- ape/loader.h | 15 +- ape/loader.lds | 5 +- build/bootstrap/ape | Bin 4096 -> 0 bytes build/bootstrap/ape.elf | Bin 0 -> 4224 bytes build/bootstrap/apetest.com | Bin 0 -> 20480 bytes build/sanitycheck | 26 ++- build/sanitycheck2 | 16 -- libc/calls/getprogramexecutablename.greg.c | 20 +- libc/calls/reservefd.c | 2 +- libc/calls/write-nt.c | 23 ++- libc/intrin/kdos2errno.S | 2 +- libc/log/showcrashreports.c | 4 +- libc/runtime/getinterpreterexecutablename.c | 3 + libc/runtime/getsymboltable.c | 10 +- libc/runtime/mmap.c | 17 +- libc/sysv/consts.sh | 53 ++---- libc/sysv/consts/MAP_32BIT.S | 2 +- libc/sysv/consts/MAP_ANON.S | 2 +- libc/sysv/consts/MAP_DENYWRITE.S | 2 +- libc/sysv/consts/MAP_EXECUTABLE.S | 2 +- libc/sysv/consts/MAP_FILE.S | 2 +- libc/sysv/consts/MAP_FIXED_NOREPLACE.S | 2 +- libc/sysv/consts/MAP_HASSEMAPHORE.S | 2 + libc/sysv/consts/MAP_INHERIT.S | 2 + libc/sysv/consts/MAP_NOCORE.S | 2 +- libc/sysv/consts/MAP_NOSYNC.S | 2 + libc/sysv/consts/MAP_POPULATE.S | 2 +- libc/sysv/consts/MINSIGSTKSZ.S | 2 +- libc/sysv/consts/SIGSTKSZ.S | 2 +- libc/sysv/consts/ST_MANDLOCK.S | 2 +- libc/sysv/consts/ST_SYNCHRONOUS.S | 2 +- libc/sysv/consts/ST_WRITE.S | 2 +- libc/sysv/consts/S_IEXEC.S | 2 - libc/sysv/consts/S_IFBLK.S | 2 - libc/sysv/consts/S_IFCHR.S | 2 - libc/sysv/consts/S_IFDIR.S | 2 - libc/sysv/consts/S_IFIFO.S | 2 - libc/sysv/consts/S_IFLNK.S | 2 - libc/sysv/consts/S_IFMT.S | 2 - libc/sysv/consts/S_IFREG.S | 2 - libc/sysv/consts/S_IFSOCK.S | 2 - libc/sysv/consts/S_IREAD.S | 2 - libc/sysv/consts/S_IRGRP.S | 2 - libc/sysv/consts/S_IROTH.S | 2 - libc/sysv/consts/S_IRUSR.S | 2 - libc/sysv/consts/S_IRWXG.S | 2 - libc/sysv/consts/S_IRWXO.S | 2 - libc/sysv/consts/S_IRWXU.S | 2 - libc/sysv/consts/S_ISGID.S | 2 - libc/sysv/consts/S_ISUID.S | 2 - libc/sysv/consts/S_ISVTX.S | 2 - libc/sysv/consts/S_IWGRP.S | 2 - libc/sysv/consts/S_IWOTH.S | 2 - libc/sysv/consts/S_IWRITE.S | 2 - libc/sysv/consts/S_IWUSR.S | 2 - libc/sysv/consts/S_IXGRP.S | 2 - libc/sysv/consts/S_IXOTH.S | 2 - libc/sysv/consts/S_IXUSR.S | 2 - libc/sysv/consts/map.h | 48 ++--- libc/sysv/consts/ss.h | 9 +- test/libc/release/test.mk | 10 +- test/libc/runtime/ape_test.c | 107 +++++++++++ test/libc/runtime/test.mk | 12 +- test/tool/plinko/plinko_test.c | 28 +-- tool/build/lib/apetest.c | 23 +++ tool/build/lib/buildlib.mk | 35 +++- tool/net/redbean.c | 1 - 75 files changed, 759 insertions(+), 443 deletions(-) delete mode 100644 build/bootstrap/ape create mode 100755 build/bootstrap/ape.elf create mode 100755 build/bootstrap/apetest.com delete mode 100755 build/sanitycheck2 create mode 100644 libc/sysv/consts/MAP_HASSEMAPHORE.S create mode 100644 libc/sysv/consts/MAP_INHERIT.S create mode 100644 libc/sysv/consts/MAP_NOSYNC.S delete mode 100644 libc/sysv/consts/S_IEXEC.S delete mode 100644 libc/sysv/consts/S_IFBLK.S delete mode 100644 libc/sysv/consts/S_IFCHR.S delete mode 100644 libc/sysv/consts/S_IFDIR.S delete mode 100644 libc/sysv/consts/S_IFIFO.S delete mode 100644 libc/sysv/consts/S_IFLNK.S delete mode 100644 libc/sysv/consts/S_IFMT.S delete mode 100644 libc/sysv/consts/S_IFREG.S delete mode 100644 libc/sysv/consts/S_IFSOCK.S delete mode 100644 libc/sysv/consts/S_IREAD.S delete mode 100644 libc/sysv/consts/S_IRGRP.S delete mode 100644 libc/sysv/consts/S_IROTH.S delete mode 100644 libc/sysv/consts/S_IRUSR.S delete mode 100644 libc/sysv/consts/S_IRWXG.S delete mode 100644 libc/sysv/consts/S_IRWXO.S delete mode 100644 libc/sysv/consts/S_IRWXU.S delete mode 100644 libc/sysv/consts/S_ISGID.S delete mode 100644 libc/sysv/consts/S_ISUID.S delete mode 100644 libc/sysv/consts/S_ISVTX.S delete mode 100644 libc/sysv/consts/S_IWGRP.S delete mode 100644 libc/sysv/consts/S_IWOTH.S delete mode 100644 libc/sysv/consts/S_IWRITE.S delete mode 100644 libc/sysv/consts/S_IWUSR.S delete mode 100644 libc/sysv/consts/S_IXGRP.S delete mode 100644 libc/sysv/consts/S_IXOTH.S delete mode 100644 libc/sysv/consts/S_IXUSR.S create mode 100644 test/libc/runtime/ape_test.c create mode 100644 tool/build/lib/apetest.c diff --git a/ape/ape.S b/ape/ape.S index acddc38a0..c7f02a217 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -550,25 +550,25 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // extract the loader into a temp folder, and use it to // load the APE without modifying it. .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" - .ascii "if [ ! -x \"$t\" ]; then\n" - .ascii "if [ ! -d /Applications ]; then\n" - .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" - .shstub ape_loader_dd_skip,2 - .ascii "\" count=\"" - .shstub ape_loader_dd_count,2 - .ascii "\" bs=64\n" -#if SupportsXnu() && defined(APE_LOADER_MACHO) - .ascii "else\n" - .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" - .shstub ape_loader_macho_dd_skip,2 - .ascii "\" count=\"" - .shstub ape_loader_macho_dd_count,2 - .ascii "\" bs=64\n" -#endif /* APE_LOADER_MACHO */ - .ascii "fi 2>/dev/null &&\n" - .ascii "chmod 755 \"$t.$$\" &&\n" - .ascii "mv \"$t.$$\" \"$t\"\n" - .ascii "fi\n" + .ascii "[ -x \"$t\" ] || {\n" + .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" + .shstub ape_loader_dd_skip,2 + .ascii "\" count=\"" + .shstub ape_loader_dd_count,2 + .ascii "\" bs=64 2>/dev/null\n" +#if SupportsXnu() + .ascii "[ -d /Applications ] && " + .ascii "dd if=\"$t.$$\"" + .ascii " of=\"$t.$$\"" + .ascii " skip=6" + .ascii " count=10" + .ascii " bs=64" + .ascii " conv=notrunc" + .ascii " 2>/dev/null\n" +#endif /* SupportsXnu() */ + .ascii "chmod 755 \"$t.$$\"\n" + .ascii "mv -f \"$t.$$\" \"$t\"\n" + .ascii "}\n" .ascii "exec \"$t\" \"$o\" \"$@\"\n" #endif /* APE_LOADER */ #ifndef APE_NO_MODIFY_SELF @@ -589,15 +589,40 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // then permission clashes can happen between system users, // since only root is able to set the sticky bit, which can // be addressed simply by overriding the TMPDIR environment - .ascii "o=\"${TMPDIR:-/tmp}/$0\"\n" - .ascii "if [ ! -e \"$o\" ]; then\n" - .ascii "d=\"$o\"\n" - .ascii "o=\"$o.$$\"\n" - .ascii "mkdir -p \"${o%/*}\" 2>/dev/null\n" - .ascii "cp -f \"$0\" \"$o\" || exit 120\n" + .ascii "t=\"${TMPDIR:-/tmp}/$0\"\n" + .ascii "[ -e \"$t\" ] || {\n" + .ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n" + .ascii "cp -f \"$o\" \"$t.$$\" &&\n" + .ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n" + .ascii "o=\"$t\"\n" #endif /* APE_NO_MODIFY_SELF */ + .ascii "exec 7<> \"$o\" || exit 121\n" + .ascii "printf '" + .ascii "\\177ELF" # 0x0: ⌂ELF + .ascii "\\2" # 4: long mode + .ascii "\\1" # 5: little endian + .ascii "\\1" # 6: elf v1.o + .ascii "\\011" # 7: FreeBSD + .ascii "\\0" # 8: os/abi ver. + .ascii "\\0\\0\\0" # 9: padding 3/7 + .ascii "\\0\\0\\0\\0" # padding 4/7 + .ascii "\\2\\0" # 10: εxεcµταblε + .ascii "\\076\\0" # 12: NexGen32e + .ascii "\\1\\0\\0\\0" # 14: elf v1.o + .shstub ape_elf_entry,8 # 18: e_entry + .shstub ape_elf_phoff,8 # 20: e_phoff + .shstub ape_elf_shoff,8 # 28: e_shoff + .ascii "\\0\\0\\0\\0" # 30: e_flags + .ascii "\\100\\0" # 34: e_ehsize + .ascii "\\070\\0" # 36: e_phentsize + .shstub ape_elf_phnum,2 # 38: e_phnum + .ascii "\\0\\0" # 3a: e_shentsize + .shstub ape_elf_shnum,2 # 3c: e_shnum + .shstub ape_elf_shstrndx,2 # 3e: e_shstrndx + .ascii "' >&7\n" + .ascii "exec 7<&-\n" #if SupportsXnu() - .ascii "if [ -d /Applications ]; then\n" + .ascii "[ -d /Applications ] && " .ascii "dd if=\"$o\"" .ascii " of=\"$o\"" .ascii " bs=8" @@ -606,54 +631,20 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .ascii "\" count=\"" .shstub ape_macho_dd_count,2 .ascii "\" conv=notrunc 2>/dev/null\n" - .ascii "el" #endif /* XNU */ - .ascii "if exec 7<> \"$o\"; then\n" - .ascii "printf '" - .ascii "\\177ELF" # 0x0: ⌂ELF - .ascii "\\2" # 4: long mode - .ascii "\\1" # 5: little endian - .ascii "\\1" # 6: elf v1.o - .ascii "\\011" # 7: FreeBSD - .ascii "\\0" # 8: os/abi ver. - .ascii "\\0\\0\\0" # 9: padding 3/7 - .ascii "\\0\\0\\0\\0" # padding 4/7 - .ascii "\\2\\0" # 10: εxεcµταblε - .ascii "\\076\\0" # 12: NexGen32e - .ascii "\\1\\0\\0\\0" # 14: elf v1.o - .shstub ape_elf_entry,8 # 18: e_entry - .shstub ape_elf_phoff,8 # 20: e_phoff - .shstub ape_elf_shoff,8 # 28: e_shoff - .ascii "\\0\\0\\0\\0" # 30: e_flags - .ascii "\\100\\0" # 34: e_ehsize - .ascii "\\070\\0" # 36: e_phentsize - .shstub ape_elf_phnum,2 # 38: e_phnum - .ascii "\\0\\0" # 3a: e_shentsize - .shstub ape_elf_shnum,2 # 3c: e_shnum - .shstub ape_elf_shstrndx,2 # 3e: e_shstrndx - .ascii "' >&7\n" - .ascii "exec 7<&-\n" - .ascii "else\n" - .ascii "exit 121\n" - .ascii "fi\n" #ifndef APE_NO_MODIFY_SELF - .ascii "exec \"$0\" \"$@\"\n" # optimistic execution + .ascii "exec \"$0\" \"$@\"\n" # try to preserve argv[0] #else - .ascii "mv -f \"$o\" \"$d\" 2>/dev/null\n" - .ascii "o=\"$d\"\n" - .ascii "fi\n" + .ascii "}\n" + .ascii "o=\"$t\"\n" .ascii "exec \"$o\" \"$@\"\n" #endif /* APE_NO_MODIFY_SELF */ .ascii "R=$?\n" - .ascii "\n" - .ascii "if [ $R -eq 126 ] && [ \"$(uname -m)\" != x86_64 ]; then\n" - .ascii "if Q=\"$(command -v qemu-x86_64)\"; then\n" + .ascii "if [ \"$(uname -m)\" != x86_64 ]; then\n" + .ascii "Q=\"$(command -v qemu-x86_64)\" &&\n" .ascii "exec \"$Q\" \"$o\" \"$@\"\n" - .ascii "else\n" - .ascii "echo error: need qemu-x86_64 >&2\n" - .ascii "fi\n" #ifndef APE_NO_MODIFY_SELF - .ascii "elif [ $R -eq 127 ]; then\n" # means argv[0] was wrong + .ascii "else\n" # means argv[0] was wrong .ascii " exec \"$o\" \"$@\"\n" # so do a path resolution #endif /* APE_NO_MODIFY_SELF */ .ascii "fi\n" @@ -661,14 +652,16 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .endobj apesh #ifdef APE_LOADER .section .ape.loader,"a",@progbits + .align 64 +ape_loader: .incbin APE_LOADER + .endobj ape_loader,globl + .align 64 +ape_loader_end: + nop + .endobj ape_loader_end,globl .previous #endif /* APE_LOADER */ -#if SupportsXnu() && defined(APE_LOADER_MACHO) - .section .ape.loader-macho,"a",@progbits - .incbin APE_LOADER_MACHO - .previous -#endif /* APE_LOADER_MACHO */ #endif /* SupportsWindows() || SupportsMetal() || SupportsXnu() */ #if SupportsSystemv() || SupportsMetal() @@ -1521,12 +1514,9 @@ kernel: movabs $ape_stack_vaddr,%rsp .byte 0x0f,0x1f,0207 # nop rdi binbase .long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512 #endif - .weak __hostos - ezlea __hostos,ax - test %rax,%rax - jz 1f - movb $METAL,(%rax) -1: push $0 + push $METAL # sets __hostos in crt.S + pop %rcx + push $0 mov %rsp,%rbp mov .Lenv0(%rip),%rax mov %rax,(%rbp) # envp[0][0] @@ -1542,7 +1532,6 @@ kernel: movabs $ape_stack_vaddr,%rsp push $1 # argc xor %ebp,%ebp xor %eax,%eax - xor %ecx,%ecx xor %edx,%edx xor %edi,%edi xor %esi,%esi diff --git a/ape/ape.lds b/ape/ape.lds index f37234a7b..e97b96058 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -243,10 +243,12 @@ SECTIONS { /* Code that needs to be addressable in Real Mode */ *(.text.real) KEEP(*(SORT_BY_NAME(.sort.text.real.*))) + /* Code we want earlier in the binary w/o modifications */ + KEEP(*(.ape.loader)) HIDDEN(_ereal = .); - . += 1; - /*END: realmode addressability guarantee */ +/*BEGIN: morphable code */ + . += 1; /* Normal Code */ *(.start) @@ -283,6 +285,7 @@ SECTIONS { /* Privileged code invulnerable to magic */ KEEP(*(.ape.pad.privileged)); . += . > 0 ? 1 : 0; +/*END: morphable code */ HIDDEN(__privileged_start = .); . += . > 0 ? 1 : 0; *(.privileged) @@ -390,21 +393,6 @@ SECTIONS { } :Ram /*END: file content that's loaded by o/s */ -/*BEGIN: payload (for now, only the APE loader) */ - .payload ALIGN(64) : { - /* Loader */ - HIDDEN(ape_loader = .); - KEEP(*(.ape.loader)) - . = ALIGN(64); - HIDDEN(ape_loader_end = .); -#if SupportsXnu() - HIDDEN(ape_loader_macho = .); - KEEP(*(.ape.loader-macho)) - . = ALIGN(64); - HIDDEN(ape_loader_macho_end = .); -#endif - } -/*END: payload */ /*BEGIN: bss memory void */ .zip . : { @@ -552,13 +540,14 @@ HIDDEN(ape_bss_filesz = 0); HIDDEN(ape_bss_memsz = SIZEOF(.bss)); HIDDEN(ape_bss_align = PAGESIZE); -SHSTUB2(ape_loader_dd_skip, RVA(ape_loader) / 64); -SHSTUB2(ape_loader_dd_count, (ape_loader_end - ape_loader) / 64); - -#if SupportsXnu() -SHSTUB2(ape_loader_macho_dd_skip, RVA(ape_loader_macho) / 64); -SHSTUB2(ape_loader_macho_dd_count, (ape_loader_macho_end - ape_loader_macho) / 64); -#endif +/* we roundup here because xnu wants the file load segments page-aligned */ +/* but we don't want to add the nop padding to the ape program, so we'll */ +/* let ape.S dd read past the end of the file into the wrapping binaries */ +SHSTUB2(ape_loader_dd_skip, DEFINED(ape_loader) ? RVA(ape_loader) / 64 : 0); +SHSTUB2(ape_loader_dd_count, + DEFINED(ape_loader_end) + ? ROUNDUP(ape_loader_end - ape_loader, PAGESIZE) / 64 + : 0); #if SupportsXnu() SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8); diff --git a/ape/ape.mk b/ape/ape.mk index 590075703..759296ec4 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -22,6 +22,10 @@ APE_NO_MODIFY_SELF = \ o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape-no-modify-self.o +APE_COPY_SELF = \ + o/$(MODE)/ape/ape.lds \ + o/$(MODE)/ape/ape-copy-self.o + APELINK = \ $(COMPILE) \ -ALINK.ape \ @@ -29,6 +33,22 @@ APELINK = \ $(LINKARGS) \ $(OUTPUT_OPTION) +APE_LOADER_FLAGS = \ + -DNDEBUG \ + -iquote. \ + -Wall \ + -Wextra \ + -fpie \ + -Os \ + -ffreestanding \ + -mgeneral-regs-only \ + -mno-red-zone \ + -fno-ident \ + -fno-gnu-unique \ + -c \ + $(OUTPUT_OPTION) \ + $< + APE_FILES := $(wildcard ape/*.*) APE_HDRS = $(filter %.h,$(APE_FILES)) APE_INCS = $(filter %.inc,$(APE_FILES)) @@ -50,31 +70,55 @@ o/ape/idata.inc: \ o/$(MODE)/ape/ape-no-modify-self.o: \ ape/ape.S \ - o/$(MODE)/ape/ape \ - o/$(MODE)/ape/ape.macho - @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/ape\"" -DAPE_LOADER_MACHO="\"o/$(MODE)/ape/ape.macho\"" $< + o/$(MODE)/ape/ape.elf + @$(COMPILE) \ + -AOBJECTIFY.S \ + $(OBJECTIFY.S) \ + $(OUTPUT_OPTION) \ + -DAPE_NO_MODIFY_SELF \ + -DAPE_LOADER="\"o/$(MODE)/ape/ape.elf\"" $< + +o/$(MODE)/ape/ape-copy-self.o: \ + ape/ape.S + @$(COMPILE) \ + -AOBJECTIFY.S \ + $(OBJECTIFY.S) \ + $(OUTPUT_OPTION) \ + -DAPE_NO_MODIFY_SELF $< o/$(MODE)/ape/loader.o: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $< - + @$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -g $(APE_LOADER_FLAGS) o/$(MODE)/ape/loader-gcc.asm: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $< + @$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS) +o/$(MODE)/ape/loader-clang.asm: ape/loader.c + @$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS) -o/$(MODE)/ape/ape: \ +o/$(MODE)/ape/loader-xnu.o: ape/loader.c + @$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -g $(APE_LOADER_FLAGS) +o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c + @$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS) +o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c + @$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS) + +o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg +o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.macho.dbg + +o/$(MODE)/ape/ape.elf.dbg: \ o/$(MODE)/ape/loader.o \ o/$(MODE)/ape/loader-elf.o \ ape/loader.lds - @$(ELFLINK) -s -z max-page-size=0x10 + @$(ELFLINK) -z max-page-size=0x10 -o/$(MODE)/ape/ape.macho: \ - o/$(MODE)/ape/loader.o \ +o/$(MODE)/ape/ape.macho.dbg: \ + o/$(MODE)/ape/loader-xnu.o \ o/$(MODE)/ape/loader-macho.o \ ape/loader-macho.lds - @$(ELFLINK) -s -z max-page-size=0x10 + @$(ELFLINK) -z max-page-size=0x10 .PHONY: o/$(MODE)/ape o/$(MODE)/ape: $(APE) \ $(APE_CHECKS) \ - o/$(MODE)/ape/ape \ + o/$(MODE)/ape/ape.elf \ o/$(MODE)/ape/ape.macho \ + o/$(MODE)/ape/ape-copy-self.o \ o/$(MODE)/ape/ape-no-modify-self.o diff --git a/ape/apeinstall.sh b/ape/apeinstall.sh index d5723f130..6d2b7cd74 100755 --- a/ape/apeinstall.sh +++ b/ape/apeinstall.sh @@ -9,19 +9,19 @@ fi if [ -f o/depend ]; then # mkdeps.com build was successfully run so assume we can build echo >&2 - echo running: make -j8 o//ape/ape >&2 - make -j8 o//ape/ape || exit + echo running: make -j8 o//ape/ape.elf >&2 + make -j8 o//ape/ape.elf || exit echo done >&2 else # no evidence we can build, use prebuilt one mkdir -p o//ape || exit - cp -af build/bootstrap/ape o//ape/ape + cp -af build/bootstrap/ape.elf o//ape/ape.elf fi echo >&2 -echo installing o//ape/ape to /usr/bin/ape >&2 -echo sudo mv -f o//ape/ape /usr/bin/ape >&2 -sudo mv -f o//ape/ape /usr/bin/ape || exit +echo installing o//ape/ape.elf to /usr/bin/ape >&2 +echo sudo mv -f o//ape/ape.elf /usr/bin/ape >&2 +sudo mv -f o//ape/ape.elf /usr/bin/ape || exit echo done >&2 if [ -e /proc/sys/fs/binfmt_misc/APE ]; then diff --git a/ape/loader-elf.S b/ape/loader-elf.S index 9fafc4fd0..cbde38956 100644 --- a/ape/loader-elf.S +++ b/ape/loader-elf.S @@ -17,6 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/elf/def.h" +#include "libc/sysv/consts/prot.h" +#include "libc/macho.internal.h" +#include "libc/dce.h" #include "libc/macros.internal.h" // APE Loader Executable Structure @@ -44,48 +47,6 @@ ehdr: .ascii "\177ELF" .word 0 # e_shstrndx .endobj ehdr,globl -// Ape Loader Entrpoint -// -// This is normally called by the operating system. However it may -// be called by the Actually Portable Executables themselves, when -// re-executing a program. Just do this: -// -// memcpy(0x200000, loader) -// xor %eax,%eax -// inc %eax -// jmp 0x200000 -// -// @see APE_LOADER_ENTRY -jg47h: .org 0x47 - .endobj jg47h -_start: mov %rsp,%rsi - jmp ApeLoader - .endfn _start,globl - -// System Call Entrpoint -// -// This function is used by the APE loader to make system calls. -// We also pass a reference to this function to the APE binary's -// _start() function. It's needed because on OpenBSD, msyscall() -// restricts which pages can issue system calls, and it can only -// be called once. Therefore if we want to be load and re-load a -// binary multiple times without calling the system execve(), we -// need to be able to handover the SYSCALL function. We hardcode -// this to a fixed address, but that shouldn't be used, since we -// would ideally want to move it to a random page in the future. -// -// @see APE_LOADER_SYSCALL -sc50h: .org 0x50 - .endobj sc50h -__syscall_loader: - clc - syscall - jc 1f - ret -1: neg %rax - ret - .endfn __syscall_loader,globl - .align 8 phdrs: .long PT_LOAD # p_type .long PF_R|PF_X # p_flags @@ -94,7 +55,7 @@ phdrs: .long PT_LOAD # p_type .quad ehdr # p_paddr .quad filesz # p_filesz .quad filesz # p_memsz - .quad PAGESIZE # p_align + .quad 64 # p_align .long PT_LOAD # p_type .long PF_R|PF_W # p_flags @@ -103,7 +64,7 @@ phdrs: .long PT_LOAD # p_type .quad bss # p_paddr .quad 0 # p_filesz .quad bsssize # p_memsz - .quad PAGESIZE # p_align + .quad 64 # p_align .long PT_GNU_STACK # p_type .long PF_R|PF_W # p_flags @@ -138,3 +99,140 @@ note: .long 2f-1f 3: .long 901000000 4: .endobj note notesize = . - note + + .align 64,0 # for ape.S dd + .org 0x180 # for ape.S dd + +// APE Loader XNU Header +// +// This header is dd'd backwards by the APE shell script when +// running on Mac OS X. +// +// @see ape/ape.S +macho: .long 0xFEEDFACE+1 + .long MAC_CPU_NEXGEN32E + .long MAC_CPU_NEXGEN32E_ALL + .long MAC_EXECUTE + .long 5 # number of load commands + .long 60f-10f # size of all load commands + .long MAC_NOUNDEFS # flags + .long 0 # reserved +10: .long MAC_LC_SEGMENT_64 + .long 20f-10b # unmaps first page dir + .ascin "__PAGEZERO",16 # consistent with linux + .quad 0,0x200000,0,0 # which forbids mem <2m + .long 0,0,0,0 +20: .long MAC_LC_SEGMENT_64 + .long 30f-20b + .ascin "__TEXT",16 + .quad ehdr # vaddr + .quad 4096 # memsz + .quad 0 # file offset + .quad filesz # file size + .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot + .long PROT_EXEC|PROT_READ # initprot + .long 1 # segment section count + .long 0 # flags +210: .ascin "__text",16 # section name (.text) + .ascin "__TEXT",16 + .quad _start # vaddr + .quad textsz # memsz + .long textoff # offset + .long 6 # align 2**6 = 64 + .long 0 # reloc table offset + .long 0 # relocation count + .long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes + .long 0,0,0 # reserved +30: .long MAC_LC_SEGMENT_64 + .long 40f-30b + .ascin "__DATA",16 + .quad bss # vaddr + .quad bsssize # memsz + .quad 0 # offset + .quad 0 # file size + .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot + .long PROT_READ|PROT_WRITE # initprot + .long 1 # segment section count + .long 0 # flags +310: .ascin "__bss",16 # section name (.bss) + .ascin "__DATA",16 + .quad bss # vaddr + .quad bsssize # memsz + .long 0 # offset + .long 12 # align 2**12 = 4096 + .long 0 # reloc table offset + .long 0 # relocation count + .long MAC_S_ZEROFILL # section type & attributes + .long 0,0,0 # reserved +40: .long MAC_LC_UUID + .long 50f-40b + .quad 0x3fb29ee4ac6c87aa # uuid1 + .quad 0xdd2c9bb866d9eef8 # uuid2 +50: .long MAC_LC_UNIXTHREAD + .long 60f-50b # cmdsize + .long MAC_THREAD_NEXGEN32E # flavaflav + .long (520f-510f)/4 # count +510: .quad 0 # rax + .quad 0 # rbx + .quad 0 # rcx + .quad XNU # rdx + .quad 0 # rdi + .quad 0 # rsi + .quad 0 # rbp + .quad 0 # rsp + .quad 0 # r8 + .quad 0 # r9 + .quad 0 # r10 + .quad 0 # r11 + .quad 0 # r12 + .quad 0 # r13 + .quad 0 # r14 + .quad 0 # r15 + .quad _start # rip + .quad 0 # rflags + .quad 0 # cs + .quad 0 # fs + .quad 0 # gs +520: +60: + .endobj macho + + .align 64,0 # for ape.S dd + .org 0x400 # for ape.S dd + +// Ape Loader Entrpoint +// +// This is normally called by the operating system. However it may +// be called by the Actually Portable Executables themselves, when +// re-executing a program. Just do this: +// +// memcpy(0x200000, loader) +// lea handoff(%rip),%rcx +// lea argblock(%rip),%rsp +// jmp 0x200400 +// +// @see APE_LOADER_ENTRY +// @see ape/loader.h +_start: mov %rsp,%rsi + jmp ApeLoader + .endfn _start,globl + +// System Call Entrpoint +// +// This function is used by the APE loader to make system calls. +// We also pass a reference to this function to the APE binary's +// _start() function. It's needed because on OpenBSD, msyscall() +// restricts which pages can issue system calls, and it can only +// be called once. Therefore if we want to be load and re-load a +// binary multiple times without calling the system execve(), we +// need to be able to handover the SYSCALL function. We hardcode +// this to a fixed address, but that shouldn't be used, since we +// would ideally want to move it to a random page in the future. +__syscall_loader: + clc + syscall + jc 1f + ret +1: neg %rax + ret + .endfn __syscall_loader,globl diff --git a/ape/loader-macho.S b/ape/loader-macho.S index 656a05b87..b027760a7 100644 --- a/ape/loader-macho.S +++ b/ape/loader-macho.S @@ -41,7 +41,7 @@ macho: .long 0xFEEDFACE+1 .long 30f-20b .ascin "__TEXT",16 .quad macho # vaddr - .quad filesz # memsz + .quad 4096 # memsz .quad 0 # file offset .quad filesz # file size .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot @@ -53,7 +53,7 @@ macho: .long 0xFEEDFACE+1 .quad _start # vaddr .quad textsz # memsz .long textoff # offset - .long 3 # align 2**3 = 8 + .long 6 # align 2**3 = 64 .long 0 # reloc table offset .long 0 # relocation count .long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes @@ -112,7 +112,7 @@ macho: .long 0xFEEDFACE+1 60: .endobj macho,globl - .align 8 + .align 64 _start: mov %rsp,%rsi jmp ApeLoader .endfn _start,globl diff --git a/ape/loader-macho.lds b/ape/loader-macho.lds index c8f36b161..973c182a5 100644 --- a/ape/loader-macho.lds +++ b/ape/loader-macho.lds @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ ENTRY(_start) -OUTPUT_FORMAT(binary) SECTIONS { . = 0x200000; diff --git a/ape/loader.c b/ape/loader.c index 413641fd9..d38e4b838 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -22,7 +22,7 @@ #define TROUBLESHOOT_OS LINUX /** - * @fileoverview APE Loader for GNU/Systemd and FreeBSD/NetBSD/OpenBSD + * @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD * * We recommend using the normal APE design, where binaries assimilate * themselves once by self-modifying the first 64 bytes. If that can't @@ -30,7 +30,7 @@ * * m=tiny * make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com - * o/$m/ape/ape o/$m/examples/printargs.com + * o/$m/ape/ape.elf o/$m/examples/printargs.com * * This is an embeddable Actually Portable Executable interpreter. The * `ape/ape.S` bootloader embeds this binary inside each binary that's @@ -45,8 +45,12 @@ * so your APE loader may be installed to your system as follows: * * m=tiny - * make -j8 MODE=$m o/$m/ape/ape - * sudo cp o/$m/ape/ape /usr/bin/ape + * make -j8 MODE=$m o/$m/ape/ape.elf + * sudo cp o/$m/ape/ape.elf /usr/bin/ape + * + * For Mac OS X systems you should install the `ape.macho` executable: + * + * sudo cp o/$m/ape/ape.macho /usr/bin/ape * * Your APE loader may be used as a shebang interpreter by doing this: * @@ -57,7 +61,7 @@ * However you won't need to do that, if your APE Loader is registered * as a binfmt_misc interpreter. You can do that as follows with root: * - * sudo cp -f o/$m/ape/ape /usr/bin + * sudo cp -f o/$m/ape/ape.elf /usr/bin * f=/proc/sys/fs/binfmt_misc/register * sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f" * @@ -78,13 +82,23 @@ */ #define LINUX 1 -#define METAL 2 -#define WINDOWS 4 #define XNU 8 #define OPENBSD 16 #define FREEBSD 32 #define NETBSD 64 +#define SupportsLinux() (SUPPORT_VECTOR & LINUX) +#define SupportsXnu() (SUPPORT_VECTOR & XNU) +#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD) +#define SupportsOpenbsd() (SUPPORT_VECTOR & OPENBSD) +#define SupportsNetbsd() (SUPPORT_VECTOR & NETBSD) + +#define IsLinux() (SupportsLinux() && os == LINUX) +#define IsXnu() (SupportsXnu() && os == XNU) +#define IsFreebsd() (SupportsFreebsd() && os == FREEBSD) +#define IsOpenbsd() (SupportsOpenbsd() && os == OPENBSD) +#define IsNetbsd() (SupportsNetbsd() && os == NETBSD) + #define O_RDONLY 0 #define PROT_READ 1 #define PROT_WRITE 2 @@ -92,7 +106,7 @@ #define MAP_SHARED 1 #define MAP_PRIVATE 2 #define MAP_FIXED 16 -#define MAP_ANONYMOUS (os == LINUX ? 32 : 4096) +#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096) #define AT_EXECFN_LINUX 31 #define AT_EXECFN_NETBSD 2014 #define ELFCLASS64 2 @@ -235,15 +249,15 @@ static char *Itoa(char p[21], long x) { #if TROUBLESHOOT const char *DescribeOs(int os) { - if (os == LINUX) { + if (IsLinux()) { return "GNU/SYSTEMD"; - } else if (os == XNU) { + } else if (IsXnu()) { return "XNU"; - } else if (os == FREEBSD) { + } else if (IsFreebsd()) { return "FREEBSD"; - } else if (os == OPENBSD) { + } else if (IsOpenbsd()) { return "OPENBSD"; - } else if (os == NETBSD) { + } else if (IsNetbsd()) { return "NETBSD"; } else { return "WUT"; @@ -251,40 +265,42 @@ const char *DescribeOs(int os) { } #endif -__attribute__((__noreturn__)) static void Exit(long rc, int os) { +__attribute__((__noreturn__)) static void Exit(int rc, int os) { asm volatile("call\t*%2" : /* no outputs */ - : "a"((os == LINUX ? 60 : 1) | (os == XNU ? 0x2000000 : 0)), - "D"(rc), "m"(syscall) + : "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc), + "rm"(syscall) : "memory"); __builtin_unreachable(); } -static void Close(long fd, int os) { - long ax, di; +static void Close(int fd, int os) { + int ax, di; asm volatile("call\t*%4" : "=a"(ax), "=D"(di) - : "0"((os == LINUX ? 3 : 6) | (os == XNU ? 0x2000000 : 0)), - "1"(fd), "m"(syscall) + : "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), + "rm"(syscall) : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Read(long fd, void *data, unsigned long size, int os) { - long ax, di, si, dx; +static int Read(int fd, void *data, int size, int os) { + long si; + int ax, di, dx; asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"((os == LINUX ? 0 : 3) | (os == XNU ? 0x2000000 : 0)), - "1"(fd), "2"(data), "3"(size), "m"(syscall) + : "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), + "2"(data), "3"(size), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); return ax; } -static void Write(long fd, const void *data, unsigned long size, int os) { - long ax, di, si, dx; +static void Write(int fd, const void *data, int size, int os) { + long si; + int ax, di, dx; asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"((os == LINUX ? 1 : 4) | (os == XNU ? 0x2000000 : 0)), - "1"(fd), "2"(data), "3"(size), "m"(syscall) + : "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), + "2"(data), "3"(size), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } @@ -292,58 +308,68 @@ static void Execve(const char *prog, char **argv, char **envp, int os) { long ax, di, si, dx; asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"((59) | (os == XNU ? 0x2000000 : 0)), "1"(prog), "2"(argv), - "3"(envp), "m"(syscall) + : "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv), + "3"(envp), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Access(const char *path, int mode, int os) { - long ax, dx, di, si; +static int Access(const char *path, int mode, int os) { + int ax, si; + long dx, di; asm volatile("call\t*%7" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"((os == LINUX ? 21 : 33) | (os == XNU ? 0x2000000 : 0)), - "1"(path), "2"(mode), "m"(syscall) + : "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)), + "1"(path), "2"(mode), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } -static void Msyscall(long p, long n, int os) { - long ax, di, si; - if (os == OPENBSD) { +static int Msyscall(long p, long n, int os) { + int ax; + long di, si; + if (!IsOpenbsd()) { + return 0; + } else { asm volatile("call\t*%6" : "=a"(ax), "=D"(di), "=S"(si) - : "0"(37), "1"(p), "2"(n), "m"(syscall) + : "0"(37), "1"(p), "2"(n), "rm"(syscall) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); + return ax; } } -static long Open(const char *path, long flags, long mode, int os) { - long ax, di, si, dx; +static int Open(const char *path, int flags, int mode, int os) { + long di; + int ax, dx, si; asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"((os == LINUX ? 2 : 5) | (os == XNU ? 0x2000000 : 0)), - "1"(path), "2"(flags), "3"(mode), "m"(syscall) - : "rcx", "r8", "r9", "r10", "r11", "memory"); + : "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)), + "1"(path), "2"(flags), "3"(mode), "rm"(syscall) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } -__attribute__((__noinline__)) long Mmap(long addr, long size, long prot, - long flags, long fd, long off, int os) { - long ax; - register long flags_ asm("r10") = flags; - register long fd_ asm("r8") = fd; +__attribute__((__noinline__)) static long Mmap(long addr, long size, int prot, + int flags, int fd, long off, + int os) { + long ax, di, si, dx; + register int flags_ asm("r10") = flags; + register int fd_ asm("r8") = fd; register long off_ asm("r9") = off; asm volatile("push\t%%r9\n\t" - "call\t*%8\n\t" + "push\t%%r9\n\t" + "call\t*%7\n\t" + "pop\t%%r9\n\t" "pop\t%%r9" - : "=a"(ax) - : "0"((os == LINUX ? 9 - : os == FREEBSD ? 477 - : 197) | - (os == XNU ? 0x2000000 : 0)), - "D"(addr), "S"(size), "d"(prot), "r"(flags_), "r"(fd_), - "r"(off_), "m"(syscall) - : "rcx", "r11", "memory"); + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_), + "+r"(fd_), "+r"(off_) + : "rm"(syscall), + "0"((IsLinux() ? 9 + : IsFreebsd() ? 477 + : 197) | + (IsXnu() ? 0x2000000 : 0)), + "1"(addr), "2"(size), "3"(prot) + : "rcx", "r11", "memory", "cc"); return ax; } @@ -514,20 +540,33 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X"); } Close(fd, os); - Msyscall(code, codesize, os); + + // authorize only the loaded program to issue system calls. if this + // fails, then we pass a link to our syscall function to the program + // since it probably means a userspace program executed this loader + // and passed us a custom syscall function earlier. + if (Msyscall(code, codesize, os) != -1) { + syscall = 0; + } + #if TROUBLESHOOT Emit(TROUBLESHOOT_OS, "preparing to jump\n"); #endif - register long r8 asm("r8") = syscall; + + // we clear all the general registers we can to have some wiggle room + // to extend the behavior of this loader in the future. we don't need + // to clear the xmm registers since the ape loader should be compiled + // with the -mgeneral-regs-only flag. + register void *r8 asm("r8") = syscall; asm volatile("xor\t%%eax,%%eax\n\t" - "xor\t%%ebx,%%ebx\n\t" "xor\t%%r9d,%%r9d\n\t" "xor\t%%r10d,%%r10d\n\t" "xor\t%%r11d,%%r11d\n\t" - "xor\t%%r12d,%%r12d\n\t" - "xor\t%%r13d,%%r13d\n\t" - "xor\t%%r14d,%%r14d\n\t" - "xor\t%%r15d,%%r15d\n\t" + "xor\t%%ebx,%%ebx\n\t" // netbsd dosen't clear this + "xor\t%%r12d,%%r12d\n\t" // netbsd dosen't clear this + "xor\t%%r13d,%%r13d\n\t" // netbsd dosen't clear this + "xor\t%%r14d,%%r14d\n\t" // netbsd dosen't clear this + "xor\t%%r15d,%%r15d\n\t" // netbsd dosen't clear this "mov\t%%rdx,%%rsp\n\t" "xor\t%%edx,%%edx\n\t" "push\t%%rsi\n\t" @@ -535,7 +574,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, "xor\t%%ebp,%%ebp\n\t" "ret" : /* no outputs */ - : "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os), + : "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os), "r"(r8) : "memory"); __builtin_unreachable(); @@ -543,7 +582,8 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, struct ApeLoader *handoff) { - long rc, *auxv; + int rc; + long *auxv; struct ElfEhdr *ehdr; int c, i, fd, os, argc; char *p, *exe, *prog, **argv, **envp, *page; @@ -553,13 +593,15 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, } u; // detect freebsd - if (di) { + if (handoff) { + os = handoff->os; + } else if (SupportsFreebsd() && di) { os = FREEBSD; sp = (long *)di; - } else if (dl == XNU) { + } else if (SupportsXnu() && dl == XNU) { os = XNU; } else { - os = LINUX; + os = 0; } // extract arguments @@ -586,19 +628,18 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, // no path searching is needed exe = handoff->prog; fd = handoff->fd; - os = handoff->os; exe = handoff->prog; page = handoff->page; ehdr = (struct ElfEhdr *)handoff->page; } else { // detect openbsd - if (!auxv[0]) { + if (SupportsOpenbsd() && !os && !auxv[0]) { os = OPENBSD; } // detect netbsd - if (os == LINUX) { + if (SupportsNetbsd() && !os) { for (; auxv[0]; auxv += 2) { if (auxv[0] == AT_EXECFN_NETBSD) { os = NETBSD; @@ -607,6 +648,11 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, } } + // default operating system + if (!os) { + os = LINUX; + } + // we can load via shell, shebang, or binfmt_misc if (argc >= 3 && !StrCmp(argv[1], "-")) { // if the first argument is a hyphen then we give the user the @@ -654,11 +700,13 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, } #endif - if (Read32(page) == Read32("\177ELF") || Read32(page) == 0xFEEDFACE + 1) { + if ((IsXnu() && Read32(page) == 0xFEEDFACE + 1) || + (!IsXnu() && Read32(page) == Read32("\177ELF"))) { Close(fd, os); Execve(exe, argv, envp, os); } + // TODO(jart): Parse Mach-O for old APE binary support on XNU. for (p = page; p < page + sizeof(u.p); ++p) { if (Read64(p) != Read64("printf '")) continue; for (i = 0, p += 8; p + 3 < page + sizeof(u.p) && (c = *p++) != '\'';) { diff --git a/ape/loader.h b/ape/loader.h index d36d7a567..7080f1370 100644 --- a/ape/loader.h +++ b/ape/loader.h @@ -1,14 +1,13 @@ #ifndef COSMOPOLITAN_APE_LOADER_H_ #define COSMOPOLITAN_APE_LOADER_H_ -#define APE_LOADER_BASE 0x200000 -#define APE_LOADER_SIZE 0x200000 -#define APE_LOADER_BSS (PAGESIZE * 2) -#define APE_LOADER_STACK 0x7f0000000000 -#define APE_LOADER_ENTRY (APE_LOADER_BASE + 0x47) -#define APE_LOADER_SYSCALL (APE_LOADER_BASE + 0x50) -#define APE_BLOCK_BASE 0x7e0000000000 -#define APE_BLOCK_SIZE 0x000200000000 +#define APE_LOADER_BASE 0x200000 +#define APE_LOADER_SIZE 0x200000 +#define APE_LOADER_ENTRY 0x200400 +#define APE_LOADER_BSS (PAGESIZE * 2) +#define APE_LOADER_STACK 0x7f0000000000 +#define APE_BLOCK_BASE 0x7e0000000000 +#define APE_BLOCK_SIZE 0x000200000000 struct ApeLoader { int fd; diff --git a/ape/loader.lds b/ape/loader.lds index e380bdb1b..b689cbae0 100644 --- a/ape/loader.lds +++ b/ape/loader.lds @@ -17,16 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ ENTRY(_start) -OUTPUT_FORMAT(binary) SECTIONS { . = 0x200000; .text : { *(.text) *(.rodata .rodata.*) - . = ALIGN(4096); + . = ALIGN(64); } filesz = . - ehdr; + textsz = . - _start; .bss ALIGN(4096) : { bss = .; *(.bss) @@ -39,3 +39,4 @@ SECTIONS { } bsssize = SIZEOF(.bss); +textoff = _start - ehdr; diff --git a/build/bootstrap/ape b/build/bootstrap/ape deleted file mode 100644 index d8857f6f44bdfd26b55648507883c54887dfa155..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeGe{cjWJ`OdK$CnTKtFdr?Hm&#Ge+9X_-0L~Kdfe&6!X9&dsI-sdzJCMz>Q~OMa zMw_5Zy1Y5ALe(bqhoMQGv_hp#u$48iH8_qMNIybIJBmPUH?_109t|IA2r#%k@5NaM z-TnhC``+j4eZHUP+$u_IO)Q(kWIiTlCBp)|jzI?HdEm z!)=vw7}Xcv4Q@<9!`y9oy_VCA1^ zBGwHoaWE^%}$0-V&~QPoUYld ztE-ZCbyl)iPj=4cu|Al-gf-bQ4P3B;K{5sou;9Q*HemXPCuokkE8)S3sgtcnxCa+A zpwQiAHi_*?nv-PuI(GKQ$}qWx3wv?lRm=`y=e0QEdS-KO?Z+nt<<|V>EG329Tu+`= zaGp(AvEG}$C>&wkB-v*u%l=N8q~oCET7v1@@D_ry^d>KH&SA%Kcohf-b_vbR2XXRc=Vk|ui%H$l93xGHS+DN&#Ju$WolQDjLhp=7P z(QgsqHX}SY`prFE=f{CF@+Cm+$;1XpEm#apNh^AS8oMZTnHPAqvIUU+xs?_2EWGcI zp2aZD7NOI<5aR1ge~QUxRKx3&8uEp>Wy18*Dy;t8 z#t%!Hr53hVSKeHqtIXzFaV?S4J|O4P(M`Kn3?^1%Wh6^d1M|RBgq6N|m}HH<3Hg(k z7~SuH%Og4e`hNG*ZqV(&d7QS;X;1*Mt+8TljZGpqJfwfL9(3Tq1fNvyvM1t{ovVSAJH~>AR49vttazIJlj(O?1p!5%*baX%H)KAL=9(~ZGTv_4m80Xs4a2JYn z8d@qdFZf&mcpsNVA)^~Dj73hfO+=S<+7tDb4%&|#f}?#ct%SE=lB>bfd^Ado(x zH*FG>FW5sS*(@mUn+~yZJ|=k%fX$ffg~@T=jIVN&?G6VnyocE{p1?=2X(Y!ykst6V zr}dEvXw+xv7{gd4F-f(|05d-UGZLY%mHJz{4*rb5lyU9Lpr^g6cLaO9=UtzrX;ZX;54l)2uR<#z|0;?=UE*J8{uCB7VuJ-Xnep{ujDlrplZX27P z;i@RJC5#t0tf^jW`i!fy=r42NUPm&KjWx?0sD?xex+#fRj>7@lysgp;V(3Ro%`#3jZXaj<8P1 zOSFnwK5a&l8odp9+gJ5I>>4_1K(7Hh*Zvv^js6K<4F0AN3dLg4SOsF>i|YLi;ZTs0 zBGD=)mno@hh*Xp$;<1u&G~f?^hXS1`ViIxx&QJxSS`ey!X2UwPO?YPAX8vjVEPcAT zxOhh{aOj816#bXXQbUHHlo>o4IMbdwHh44~Zb40hZ^h2E4<4-x4<18<$My^!3#5+y zhY*hXgP|DO%@;>=1JS0ISi{a;GAdnKT8e&}h|3L;5b}pZd;F1L%#UKxx=>7xBRLU? zgj#ZU$#PS?qNF4vSR9UqOTe$rmn(4LS16$k5fGEw;J$izbaAR|YkDBkD~hZ#sT3f0Oj5b)wEB>HN_t>T&t zwb38gMO~x}Z|!<<>pF4$4@<=iw;>pc$lF9u?HWngv}uPSC9O4(Wn~Y_xCNguB|_m+ zY13*$rfF9&7Dtf~)KQKi2%#Z@>KkHl88tz*j0hn!A%7S_D1n6RuM3A5p;|PA$h#wXas%Ah->s ysJR4HudUt61fq#>5E(gc0F9ZKQz%^jfRFXX+@f~6Gnj|a!yG*9gYV74fqw%EhS9A6 diff --git a/build/bootstrap/ape.elf b/build/bootstrap/ape.elf new file mode 100755 index 0000000000000000000000000000000000000000..b6da8fcb391abaffac38ac42011abfd7f9f9823a GIT binary patch literal 4224 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 literal 0 HcmV?d00001 diff --git a/build/bootstrap/apetest.com b/build/bootstrap/apetest.com new file mode 100755 index 0000000000000000000000000000000000000000..f19ee992d3da4de600ac219573db37a5549d73f4 GIT binary patch literal 20480 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 literal 0 HcmV?d00001 diff --git a/build/sanitycheck b/build/sanitycheck index 7a06eb647..3bd95062f 100755 --- a/build/sanitycheck +++ b/build/sanitycheck @@ -27,28 +27,38 @@ EOF exit 1 fi -build/sanitycheck2 -if [ $? -ne 123 ]; then +if [ ! -f /proc/sys/fs/binfmt_misc/status ]; then + exit 0 +fi + +STATUS="$(build/bootstrap/apetest.com)" +if [ x"$STATUS" != xsuccess ]; then cat <<'EOF' >&2 ERROR - Thompson Shell Backwards Compatibility Issue Detected + APE Execution error. DETAILS - Your system has likely been configured to use binfmt_misc and wine. - You need to run the command below which will install a /usr/bin/ape - program and then register it with binfmt_misc. See ape/loader.c for - source code and technical details. + Your system has probably been configured to use binfmt_misc. You need + to run the command below to install /usr/bin/ape and register it with + binfmt_misc. See ape/loader.c for source code, and technical details. WORKAROUND ape/apeinstall.sh +NOTES + + If it still doesn't work, possibly due to an overly aggressive WINE + or WSL registration, or possibly due to the ordering of definitions, + then one troubleshooting step is to just unregister everything using + sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status' and try again. + SEE ALSO - https://justine.storage.googleapis.com/ape.html + https://justine.lol/ape.html EOF kill $1 diff --git a/build/sanitycheck2 b/build/sanitycheck2 deleted file mode 100755 index 4e74e3964..000000000 --- a/build/sanitycheck2 +++ /dev/null @@ -1,16 +0,0 @@ -MZboop=123 -exit $MZboop - - - - - - - - - - - - - - diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index f3ecbab09..b6ab8a1e6 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -34,12 +34,22 @@ char program_executable_name[PATH_MAX]; +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) { char *q; ssize_t rc; size_t i, n; union { int cmd[4]; + char path[PATH_MAX]; char16_t path16[PATH_MAX]; } u; @@ -61,7 +71,11 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { return; } - if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) { + // 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 (__argc && (((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 != '/') { if (q[0] == '.' && q[1] == '/') { q += 2; @@ -78,12 +92,12 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { return; } + // 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; return; } - if (IsFreebsd() || IsNetbsd()) { u.cmd[0] = CTL_KERN; u.cmd[1] = KERN_PROC; @@ -101,7 +115,7 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { } /** - * Returns absolute path of executable. + * Returns absolute path of program. */ char *GetProgramExecutableName(void) { int e; diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index a8ad44180..81a0d45c9 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -58,7 +58,7 @@ int __ensurefds_unlocked(int fd) { if (!(p2 = weaken(malloc)(n2 * sizeof(*p1)))) return -1; __cxa_atexit(FreeOldFdsArray, p1, 0); memcpy(p2, p1, n1 * sizeof(*p1)); - bzero(p2 + n1, (p2 + n2) - (p2 + n1)); + bzero(p2 + n1, (n2 - n1) * sizeof(*p1)); g_fds.p = p2; g_fds.n = n2; return fd; diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 4999f6ce8..5ddc51988 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -16,10 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/runtime.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" @@ -37,12 +42,18 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, // return ebadf(); /* handled by consts.sh */ // case kNtErrorNotEnoughQuota: // return edquot(); /* handled by consts.sh */ - case kNtErrorBrokenPipe: // broken pipe - case kNtErrorNoData: // closing named pipe - __sig_raise(SIGPIPE, SI_KERNEL); // - return epipe(); // - case kNtErrorAccessDenied: // write doesn't return EACCESS - return ebadf(); // + case kNtErrorBrokenPipe: // broken pipe + case kNtErrorNoData: // closing named pipe + if (weaken(__sig_raise)) { + weaken(__sig_raise)(SIGPIPE, SI_KERNEL); + return epipe(); + } else { + STRACE("broken pipe"); + __restorewintty(); + _Exit(128 + EPIPE); + } + case kNtErrorAccessDenied: // write doesn't return EACCESS + return ebadf(); // default: return __winerr(); } diff --git a/libc/intrin/kdos2errno.S b/libc/intrin/kdos2errno.S index f09390e7f..3bab83afd 100644 --- a/libc/intrin/kdos2errno.S +++ b/libc/intrin/kdos2errno.S @@ -27,7 +27,7 @@ .long \systemv - kDos2Errno .endm - .section .rodata + .section .rodata,"a",@progbits .underrun kDos2Errno: // .e kNtErrorInvalidFunction,ENOSYS # in consts.sh diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 29bdfeddf..7d317b07d 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -106,8 +106,10 @@ void ShowCrashReports(void) { bzero(&ss, sizeof(ss)); ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; + // FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here + // OpenBSD sigaltstack() auto-applies MAP_STACK to the memory if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, - MAP_STACK | MAP_ANONYMOUS, -1, 0))) { + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) { if (!sigaltstack(&ss, &g_oldsigaltstack)) { __cxa_atexit(FreeSigAltStack, ss.ss_sp, 0); } else { diff --git a/libc/runtime/getinterpreterexecutablename.c b/libc/runtime/getinterpreterexecutablename.c index 2f3aa6c24..898731a1f 100644 --- a/libc/runtime/getinterpreterexecutablename.c +++ b/libc/runtime/getinterpreterexecutablename.c @@ -51,6 +51,9 @@ char *GetInterpreterExecutableName(char *p, size_t n) { if (n < 2) { errno = ENAMETOOLONG; } else if (IsWindows() || IsXnu()) { + // TODO(jart): Does XNU guarantee argv[0] is legit? + // Otherwise we should return NULL. + // What about OpenBSD? if (strlen(GetProgramExecutableName()) < n) { strcpy(p, GetProgramExecutableName()); return p; diff --git a/libc/runtime/getsymboltable.c b/libc/runtime/getsymboltable.c index cc7988072..45c8c4750 100644 --- a/libc/runtime/getsymboltable.c +++ b/libc/runtime/getsymboltable.c @@ -96,7 +96,13 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { * @note This code can't depend on dlmalloc() */ static struct SymbolTable *GetSymbolTableFromElf(void) { - return OpenSymbolTable(FindDebugBinary()); + int e; + const char *s; + if ((s = FindDebugBinary())) { + return OpenSymbolTable(s); + } else { + return 0; + } } /** @@ -117,7 +123,7 @@ static struct SymbolTable *GetSymbolTableFromElf(void) { * Function tracing is disabled throughout the duration of this call. * Backtraces and other core runtime functionality depend on this. * - * @return symbol table, or NULL w/ errno on first call + * @return symbol table, or NULL if not found */ struct SymbolTable *GetSymbolTable(void) { struct Zipos *z; diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index cb58ffb8c..32f0eade1 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -323,7 +323,7 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, clashes = OverlapsImageSpace(p, size) || OverlapsExistingMapping(p, size); - if ((flags & MAP_FIXED_NOREPLACE) && clashes) { + if ((flags & MAP_FIXED_NOREPLACE) == MAP_FIXED_NOREPLACE && clashes) { STRACE("noreplace overlaps existing"); return VIP(eexist()); } @@ -464,14 +464,21 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, * compile-time checks to ensure some char[8192] vars will not * create an undetectable overflow into another thread's stack * Your `flags` may optionally bitwise or any of the following: - * - `MAP_FIXED` in which case `addr` becomes more than a hint - * - `MAP_FIXED_NOREPLACE` to protect existing maps (Linux-only) * - `MAP_ANONYMOUS` in which case `fd == -1` should be the case + * - `MAP_FIXED` in which case `addr` becomes more than a hint + * - `MAP_FIXED_NOREPLACE` to protect existing mappings; this is + * always polyfilled by mmap() which tracks its own memory and + * removed before passing to the kernel, in order to support + * old versions; if you believe mappings exist which only the + * kernel knows, then this flag may be passed to sys_mmap() on + * Linux 4.17+ and FreeBSD (where it has multiple bits) * - `MAP_CONCEAL` is FreeBSD/NetBSD/OpenBSD-only * - `MAP_NORESERVE` is Linux/XNU/NetBSD-only - * - `MAP_LOCKED` is Linux-only - * - `MAP_POPULATE` is Linux-only + * - `MAP_POPULATE` is Linux/FreeBSD-only * - `MAP_NONBLOCK` is Linux-only + * - `MAP_NOSYNC` is FreeBSD-only + * - `MAP_INHERIT` is NetBSD-only + * - `MAP_LOCKED` is Linux-only * @param fd is an open()'d file descriptor, whose contents shall be * made available w/ automatic reading at the chosen address and * must be -1 if MAP_ANONYMOUS is specified diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 3be697aa8..cd2ffc2a6 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -224,22 +224,25 @@ syscon mmap MAP_PRIVATE 2 2 2 2 2 2 # forced consensus & faked nt syscon mmap MAP_STACK 6 6 6 6 6 6 # our definition syscon mmap MAP_TYPE 15 15 15 15 15 15 # mask for type of mapping syscon mmap MAP_FIXED 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 # unix consensus; openbsd appears to forbid; faked nt -syscon mmap MAP_FIXED_NOREPLACE 0x08000000 0x08000000 0x08000000 0x08000000 0x08000000 0x08000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+ +syscon mmap MAP_FIXED_NOREPLACE 0x08000000 0x00004010 0x08000000 0x08000000 0x08000000 0x08000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+; MAP_FIXED|MAP_EXCL on FreeBSD syscon mmap MAP_ANONYMOUS 0x00000020 0x00001000 0x00001000 0x00001000 0x00001000 0x00000020 # bsd consensus; faked nt syscon mmap MAP_GROWSDOWN 0x00000100 0 0 0 0 0 # use MAP_STACK; abstracted by MAP_STACK; may be passed to __sys_mmap() for low-level Linux fiddling -syscon mmap MAP_CONCEAL 0 0 0x00020000 0x00008000 0x00008000 0 # omit from core dumps; MAP_NOCORE on FreeBSD syscon mmap MAP_LOCKED 0x00002000 0 0 0 0 0 syscon mmap MAP_NORESERVE 0x00004000 0x00000040 0 0 0x00000040 0 # Linux calls it "reserve"; NT calls it "commit"? which is default? -syscon mmap MAP_POPULATE 0x00008000 0 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping +syscon mmap MAP_POPULATE 0x00008000 0 0x00040000 0 0 0 # MAP_PREFAULT_READ on FreeBSD; can avoid madvise(MADV_WILLNEED) on private file mapping syscon mmap MAP_NONBLOCK 0x00010000 0 0 0 0 0 syscon mmap MAP_HUGETLB 0x00040000 0 0 0 0 0x80000000 # kNtSecLargePages +syscon mmap MAP_INHERIT -1 -1 -1 -1 0x00000080 -1 # make it inherit across execve() +syscon mmap MAP_HASSEMAPHORE 0 0x00000200 0x00000200 0 0x00000200 0 # does it matter on x86? +syscon mmap MAP_NOSYNC 0 0 0x00000800 0 0 0 # flush to physical media only when necessary rather than gratuitously; be sure to use write() rather than ftruncate() with this! +syscon mmap MAP_CONCEAL 0 0 0x00020000 0x00008000 0x00008000 0 # omit from core dumps; MAP_NOCORE on FreeBSD syscon mmap MAP_HUGE_MASK 63 0 0 0 0 0 syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 0 -syscon compat MAP_NOCORE 0 0 0x0020000 0x8000 0x8000 0 # use MAP_CONCEAL -syscon compat MAP_ANON 0x20 0x1000 0x0001000 0x1000 0x1000 0x20 # bsd consensus; faked nt -syscon compat MAP_EXECUTABLE 0x1000 0 0 0 0 0 # ignored -syscon compat MAP_DENYWRITE 0x0800 0 0 0 0 0 -syscon compat MAP_32BIT 0x40 0 0x080000 0 0 0 # iffy +syscon compat MAP_NOCORE 0 0 0x00020000 0x00008000 0x00008000 0 # use MAP_CONCEAL +syscon compat MAP_ANON 0x00000020 0x00001000 0x00001000 0x00001000 0x00001000 0x00000020 # bsd consensus; faked nt +syscon compat MAP_EXECUTABLE 0x00001000 0 0 0 0 0 # ignored +syscon compat MAP_DENYWRITE 0x00000800 0 0 0 0 0 +syscon compat MAP_32BIT 0x00000040 0 0x00080000 0 0 0 # iffy # madvise() flags # @@ -337,36 +340,6 @@ syscon waitid WEXITED 4 4 0x10 0 32 0 syscon waitid WSTOPPED 2 8 2 0 2 0 syscon waitid WNOWAIT 0x01000000 0x20 8 0 0x10000 0 -# stat::st_mode constants -# -# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon stat S_IFREG 0100000 0100000 0100000 0100000 0100000 0100000 # regular file (unix consensus; faked nt) -syscon stat S_IFBLK 0060000 0060000 0060000 0060000 0060000 0060000 # block device (unix consensus; faked nt) -syscon stat S_IFCHR 0020000 0020000 0020000 0020000 0020000 0020000 # character device (unix consensus; faked nt) -syscon stat S_IFDIR 0040000 0040000 0040000 0040000 0040000 0040000 # directory (unix consensus; faked nt) -syscon stat S_IFIFO 0010000 0010000 0010000 0010000 0010000 0010000 # pipe (unix consensus; faked nt) -syscon stat S_IFLNK 0120000 0120000 0120000 0120000 0120000 0120000 # symbolic link (unix consensus; faked nt) -syscon stat S_IFSOCK 0140000 0140000 0140000 0140000 0140000 0140000 # socket (unix consensus; faked nt) -syscon stat S_IFMT 0170000 0170000 0170000 0170000 0170000 0170000 # FILE TYPE MASK (unix consensus; faked nt) -syscon stat S_ISVTX 0001000 0001000 0001000 0001000 0001000 0001000 # THE STICKY BIT (unix consensus; faked nt) -syscon stat S_ISGID 0002000 0002000 0002000 0002000 0002000 0002000 # the setgid bit (unix consensus; faked nt) -syscon stat S_ISUID 0004000 0004000 0004000 0004000 0004000 0004000 # the setuid bit (unix consensus; faked nt) -syscon stat S_IEXEC 0000100 0000100 0000100 0000100 0000100 0000100 # just use octal (unix consensus; faked nt) -syscon stat S_IWRITE 0000200 0000200 0000200 0000200 0000200 0000200 # just use octal (unix consensus; faked nt) -syscon stat S_IREAD 0000400 0000400 0000400 0000400 0000400 0000400 # just use octal (unix consensus; faked nt) -syscon stat S_IXUSR 0000100 0000100 0000100 0000100 0000100 0000100 # just use octal (unix consensus; faked nt) -syscon stat S_IWUSR 0000200 0000200 0000200 0000200 0000200 0000200 # just use octal (unix consensus; faked nt) -syscon stat S_IRUSR 0000400 0000400 0000400 0000400 0000400 0000400 # just use octal (unix consensus; faked nt) -syscon stat S_IRWXU 0000700 0000700 0000700 0000700 0000700 0000700 # just use octal (unix consensus; faked nt) -syscon stat S_IXGRP 0000010 0000010 0000010 0000010 0000010 0000010 # just use octal (unix consensus; faked nt) -syscon stat S_IWGRP 0000020 0000020 0000020 0000020 0000020 0000020 # just use octal (unix consensus; faked nt) -syscon stat S_IRGRP 0000040 0000040 0000040 0000040 0000040 0000040 # just use octal (unix consensus; faked nt) -syscon stat S_IRWXG 0000070 0000070 0000070 0000070 0000070 0000070 # just use octal (unix consensus; faked nt) -syscon stat S_IXOTH 0000001 0000001 0000001 0000001 0000001 0000001 # just use octal (unix consensus; faked nt) -syscon stat S_IWOTH 0000002 0000002 0000002 0000002 0000002 0000002 # just use octal (unix consensus; faked nt) -syscon stat S_IROTH 0000004 0000004 0000004 0000004 0000004 0000004 # just use octal (unix consensus; faked nt) -syscon stat S_IRWXO 0000007 0000007 0000007 0000007 0000007 0000007 # just use octal (unix consensus; faked nt) - # fcntl() # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary @@ -605,7 +578,8 @@ syscon sicode SYS_USER_DISPATCH 2 -1 -1 -1 -1 -1 # SIGSYS; syscall # sigaltstack() values # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon ss SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0x7000 0x2000 +syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlayed with STACKSIZE; you need to #undef SIGSTKSZ to access this symbol +syscon ss MINSIGSTKSZ 2048 32768 2048 12288 8192 2048 # overlayed with 32768; you need to #undef MINSIGSTKSZ to access this symbol syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus @@ -3131,7 +3105,6 @@ syscon misc CSTATUS 0 20 20 255 255 0 syscon misc DEAD_PROCESS 8 8 7 0 0 0 syscon misc FNM_NOSYS -1 -1 -1 2 2 0 syscon misc INIT_PROCESS 5 5 5 0 0 0 -syscon misc MINSIGSTKSZ 0x0800 0x8000 0x0800 0x3000 0x2000 0 syscon misc MQ_PRIO_MAX 0x8000 0 0x40 0 0 0 syscon misc MTERASE 13 0 12 9 9 0 syscon misc MTLOAD 30 0 19 0 0 0 diff --git a/libc/sysv/consts/MAP_32BIT.S b/libc/sysv/consts/MAP_32BIT.S index 69d1006da..992a099ca 100644 --- a/libc/sysv/consts/MAP_32BIT.S +++ b/libc/sysv/consts/MAP_32BIT.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_32BIT,0x40,0,0x080000,0,0,0 +.syscon compat,MAP_32BIT,0x00000040,0,0x00080000,0,0,0 diff --git a/libc/sysv/consts/MAP_ANON.S b/libc/sysv/consts/MAP_ANON.S index a1eb02bfe..a5bd6ea5b 100644 --- a/libc/sysv/consts/MAP_ANON.S +++ b/libc/sysv/consts/MAP_ANON.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_ANON,0x20,0x1000,0x0001000,0x1000,0x1000,0x20 +.syscon compat,MAP_ANON,0x00000020,0x00001000,0x00001000,0x00001000,0x00001000,0x00000020 diff --git a/libc/sysv/consts/MAP_DENYWRITE.S b/libc/sysv/consts/MAP_DENYWRITE.S index 98efad2ec..3f495ded7 100644 --- a/libc/sysv/consts/MAP_DENYWRITE.S +++ b/libc/sysv/consts/MAP_DENYWRITE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_DENYWRITE,0x0800,0,0,0,0,0 +.syscon compat,MAP_DENYWRITE,0x00000800,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_EXECUTABLE.S b/libc/sysv/consts/MAP_EXECUTABLE.S index e2efd0d26..078c5b380 100644 --- a/libc/sysv/consts/MAP_EXECUTABLE.S +++ b/libc/sysv/consts/MAP_EXECUTABLE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_EXECUTABLE,0x1000,0,0,0,0,0 +.syscon compat,MAP_EXECUTABLE,0x00001000,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_FILE.S b/libc/sysv/consts/MAP_FILE.S index 13a1fe32c..55800b2c4 100644 --- a/libc/sysv/consts/MAP_FILE.S +++ b/libc/sysv/consts/MAP_FILE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_FILE,0,0,0,0,0,0 +.syscon mmap,MAP_FILE,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_FIXED_NOREPLACE.S b/libc/sysv/consts/MAP_FIXED_NOREPLACE.S index 90b4f3369..0056e9e66 100644 --- a/libc/sysv/consts/MAP_FIXED_NOREPLACE.S +++ b/libc/sysv/consts/MAP_FIXED_NOREPLACE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_FIXED_NOREPLACE,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000 +.syscon mmap,MAP_FIXED_NOREPLACE,0x08000000,0x00004010,0x08000000,0x08000000,0x08000000,0x08000000 diff --git a/libc/sysv/consts/MAP_HASSEMAPHORE.S b/libc/sysv/consts/MAP_HASSEMAPHORE.S new file mode 100644 index 000000000..807f606aa --- /dev/null +++ b/libc/sysv/consts/MAP_HASSEMAPHORE.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon mmap,MAP_HASSEMAPHORE,0,0x00000200,0x00000200,0,0x00000200,0 diff --git a/libc/sysv/consts/MAP_INHERIT.S b/libc/sysv/consts/MAP_INHERIT.S new file mode 100644 index 000000000..7216389f8 --- /dev/null +++ b/libc/sysv/consts/MAP_INHERIT.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon mmap,MAP_INHERIT,-1,-1,-1,-1,0x00000080,-1 diff --git a/libc/sysv/consts/MAP_NOCORE.S b/libc/sysv/consts/MAP_NOCORE.S index c0e6b263f..bb2e48762 100644 --- a/libc/sysv/consts/MAP_NOCORE.S +++ b/libc/sysv/consts/MAP_NOCORE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,MAP_NOCORE,0,0,0x0020000,0x8000,0x8000,0 +.syscon compat,MAP_NOCORE,0,0,0x00020000,0x00008000,0x00008000,0 diff --git a/libc/sysv/consts/MAP_NOSYNC.S b/libc/sysv/consts/MAP_NOSYNC.S new file mode 100644 index 000000000..054342f35 --- /dev/null +++ b/libc/sysv/consts/MAP_NOSYNC.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon mmap,MAP_NOSYNC,0,0,0x00000800,0,0,0 diff --git a/libc/sysv/consts/MAP_POPULATE.S b/libc/sysv/consts/MAP_POPULATE.S index 36d7843ec..bca16d04f 100644 --- a/libc/sysv/consts/MAP_POPULATE.S +++ b/libc/sysv/consts/MAP_POPULATE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_POPULATE,0x00008000,0,0,0,0,0 +.syscon mmap,MAP_POPULATE,0x00008000,0,0x00040000,0,0,0 diff --git a/libc/sysv/consts/MINSIGSTKSZ.S b/libc/sysv/consts/MINSIGSTKSZ.S index a5c500930..23a7e9ec8 100644 --- a/libc/sysv/consts/MINSIGSTKSZ.S +++ b/libc/sysv/consts/MINSIGSTKSZ.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,MINSIGSTKSZ,0x0800,0x8000,0x0800,0x3000,0x2000,0 +.syscon ss,MINSIGSTKSZ,2048,32768,2048,12288,8192,2048 diff --git a/libc/sysv/consts/SIGSTKSZ.S b/libc/sysv/consts/SIGSTKSZ.S index 1a557a4b5..9f00d1765 100644 --- a/libc/sysv/consts/SIGSTKSZ.S +++ b/libc/sysv/consts/SIGSTKSZ.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon ss,SIGSTKSZ,0x2000,0x020000,0x8800,0x7000,0x7000,0x2000 +.syscon ss,SIGSTKSZ,8192,131072,34816,28672,28672,8192 diff --git a/libc/sysv/consts/ST_MANDLOCK.S b/libc/sysv/consts/ST_MANDLOCK.S index e23e806b8..dbfbec538 100644 --- a/libc/sysv/consts/ST_MANDLOCK.S +++ b/libc/sysv/consts/ST_MANDLOCK.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon statvfs,ST_MANDLOCK,0x40,0,0,0,0,0 +.syscon statvfs,ST_MANDLOCK,0x0040,0,0,0,0,0 diff --git a/libc/sysv/consts/ST_SYNCHRONOUS.S b/libc/sysv/consts/ST_SYNCHRONOUS.S index 9e7308849..eaec0a049 100644 --- a/libc/sysv/consts/ST_SYNCHRONOUS.S +++ b/libc/sysv/consts/ST_SYNCHRONOUS.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon statvfs,ST_SYNCHRONOUS,0x10,0,0,0,2,0 +.syscon statvfs,ST_SYNCHRONOUS,16,0,0,0,2,0 diff --git a/libc/sysv/consts/ST_WRITE.S b/libc/sysv/consts/ST_WRITE.S index 1945a52f7..84a0035fd 100644 --- a/libc/sysv/consts/ST_WRITE.S +++ b/libc/sysv/consts/ST_WRITE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon statvfs,ST_WRITE,0x80,0,0,0,0,0 +.syscon statvfs,ST_WRITE,0x0080,0,0,0,0,0 diff --git a/libc/sysv/consts/S_IEXEC.S b/libc/sysv/consts/S_IEXEC.S deleted file mode 100644 index cec133e38..000000000 --- a/libc/sysv/consts/S_IEXEC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IEXEC,0000100,0000100,0000100,0000100,0000100,0000100 diff --git a/libc/sysv/consts/S_IFBLK.S b/libc/sysv/consts/S_IFBLK.S deleted file mode 100644 index bb75920eb..000000000 --- a/libc/sysv/consts/S_IFBLK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFBLK,0060000,0060000,0060000,0060000,0060000,0060000 diff --git a/libc/sysv/consts/S_IFCHR.S b/libc/sysv/consts/S_IFCHR.S deleted file mode 100644 index c01847833..000000000 --- a/libc/sysv/consts/S_IFCHR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFCHR,0020000,0020000,0020000,0020000,0020000,0020000 diff --git a/libc/sysv/consts/S_IFDIR.S b/libc/sysv/consts/S_IFDIR.S deleted file mode 100644 index 58c96c0de..000000000 --- a/libc/sysv/consts/S_IFDIR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFDIR,0040000,0040000,0040000,0040000,0040000,0040000 diff --git a/libc/sysv/consts/S_IFIFO.S b/libc/sysv/consts/S_IFIFO.S deleted file mode 100644 index 634433d4f..000000000 --- a/libc/sysv/consts/S_IFIFO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFIFO,0010000,0010000,0010000,0010000,0010000,0010000 diff --git a/libc/sysv/consts/S_IFLNK.S b/libc/sysv/consts/S_IFLNK.S deleted file mode 100644 index 7f07c7498..000000000 --- a/libc/sysv/consts/S_IFLNK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFLNK,0120000,0120000,0120000,0120000,0120000,0120000 diff --git a/libc/sysv/consts/S_IFMT.S b/libc/sysv/consts/S_IFMT.S deleted file mode 100644 index af076b5ae..000000000 --- a/libc/sysv/consts/S_IFMT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFMT,0170000,0170000,0170000,0170000,0170000,0170000 diff --git a/libc/sysv/consts/S_IFREG.S b/libc/sysv/consts/S_IFREG.S deleted file mode 100644 index 8c859e78c..000000000 --- a/libc/sysv/consts/S_IFREG.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFREG,0100000,0100000,0100000,0100000,0100000,0100000 diff --git a/libc/sysv/consts/S_IFSOCK.S b/libc/sysv/consts/S_IFSOCK.S deleted file mode 100644 index 3cc80067e..000000000 --- a/libc/sysv/consts/S_IFSOCK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IFSOCK,0140000,0140000,0140000,0140000,0140000,0140000 diff --git a/libc/sysv/consts/S_IREAD.S b/libc/sysv/consts/S_IREAD.S deleted file mode 100644 index 5e0bee1c4..000000000 --- a/libc/sysv/consts/S_IREAD.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IREAD,0000400,0000400,0000400,0000400,0000400,0000400 diff --git a/libc/sysv/consts/S_IRGRP.S b/libc/sysv/consts/S_IRGRP.S deleted file mode 100644 index ad9f3a6f8..000000000 --- a/libc/sysv/consts/S_IRGRP.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IRGRP,0000040,0000040,0000040,0000040,0000040,0000040 diff --git a/libc/sysv/consts/S_IROTH.S b/libc/sysv/consts/S_IROTH.S deleted file mode 100644 index 6179fbf38..000000000 --- a/libc/sysv/consts/S_IROTH.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IROTH,0000004,0000004,0000004,0000004,0000004,0000004 diff --git a/libc/sysv/consts/S_IRUSR.S b/libc/sysv/consts/S_IRUSR.S deleted file mode 100644 index 7720d0f60..000000000 --- a/libc/sysv/consts/S_IRUSR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IRUSR,0000400,0000400,0000400,0000400,0000400,0000400 diff --git a/libc/sysv/consts/S_IRWXG.S b/libc/sysv/consts/S_IRWXG.S deleted file mode 100644 index e35589566..000000000 --- a/libc/sysv/consts/S_IRWXG.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IRWXG,0000070,0000070,0000070,0000070,0000070,0000070 diff --git a/libc/sysv/consts/S_IRWXO.S b/libc/sysv/consts/S_IRWXO.S deleted file mode 100644 index c9a4db418..000000000 --- a/libc/sysv/consts/S_IRWXO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IRWXO,0000007,0000007,0000007,0000007,0000007,0000007 diff --git a/libc/sysv/consts/S_IRWXU.S b/libc/sysv/consts/S_IRWXU.S deleted file mode 100644 index 3a291a499..000000000 --- a/libc/sysv/consts/S_IRWXU.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IRWXU,0000700,0000700,0000700,0000700,0000700,0000700 diff --git a/libc/sysv/consts/S_ISGID.S b/libc/sysv/consts/S_ISGID.S deleted file mode 100644 index 108508726..000000000 --- a/libc/sysv/consts/S_ISGID.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_ISGID,0002000,0002000,0002000,0002000,0002000,0002000 diff --git a/libc/sysv/consts/S_ISUID.S b/libc/sysv/consts/S_ISUID.S deleted file mode 100644 index 170183946..000000000 --- a/libc/sysv/consts/S_ISUID.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_ISUID,0004000,0004000,0004000,0004000,0004000,0004000 diff --git a/libc/sysv/consts/S_ISVTX.S b/libc/sysv/consts/S_ISVTX.S deleted file mode 100644 index 33636aff5..000000000 --- a/libc/sysv/consts/S_ISVTX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_ISVTX,0001000,0001000,0001000,0001000,0001000,0001000 diff --git a/libc/sysv/consts/S_IWGRP.S b/libc/sysv/consts/S_IWGRP.S deleted file mode 100644 index 27dd51e4c..000000000 --- a/libc/sysv/consts/S_IWGRP.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IWGRP,0000020,0000020,0000020,0000020,0000020,0000020 diff --git a/libc/sysv/consts/S_IWOTH.S b/libc/sysv/consts/S_IWOTH.S deleted file mode 100644 index 61e7b7ae8..000000000 --- a/libc/sysv/consts/S_IWOTH.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IWOTH,0000002,0000002,0000002,0000002,0000002,0000002 diff --git a/libc/sysv/consts/S_IWRITE.S b/libc/sysv/consts/S_IWRITE.S deleted file mode 100644 index df2360188..000000000 --- a/libc/sysv/consts/S_IWRITE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IWRITE,0000200,0000200,0000200,0000200,0000200,0000200 diff --git a/libc/sysv/consts/S_IWUSR.S b/libc/sysv/consts/S_IWUSR.S deleted file mode 100644 index 7df8df706..000000000 --- a/libc/sysv/consts/S_IWUSR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IWUSR,0000200,0000200,0000200,0000200,0000200,0000200 diff --git a/libc/sysv/consts/S_IXGRP.S b/libc/sysv/consts/S_IXGRP.S deleted file mode 100644 index f23c82907..000000000 --- a/libc/sysv/consts/S_IXGRP.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IXGRP,0000010,0000010,0000010,0000010,0000010,0000010 diff --git a/libc/sysv/consts/S_IXOTH.S b/libc/sysv/consts/S_IXOTH.S deleted file mode 100644 index ff3cc2474..000000000 --- a/libc/sysv/consts/S_IXOTH.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IXOTH,0000001,0000001,0000001,0000001,0000001,0000001 diff --git a/libc/sysv/consts/S_IXUSR.S b/libc/sysv/consts/S_IXUSR.S deleted file mode 100644 index fb7f231b5..000000000 --- a/libc/sysv/consts/S_IXUSR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon stat,S_IXUSR,0000100,0000100,0000100,0000100,0000100,0000100 diff --git a/libc/sysv/consts/map.h b/libc/sysv/consts/map.h index 725ffac82..028aeaf9a 100644 --- a/libc/sysv/consts/map.h +++ b/libc/sysv/consts/map.h @@ -12,13 +12,17 @@ extern const long MAP_DENYWRITE; extern const long MAP_EXECUTABLE; extern const long MAP_FILE; extern const long MAP_FIXED; +extern const long MAP_FIXED_NOREPLACE; extern const long MAP_GROWSDOWN; +extern const long MAP_HASSEMAPHORE; extern const long MAP_HUGETLB; extern const long MAP_HUGE_MASK; extern const long MAP_HUGE_SHIFT; +extern const long MAP_INHERIT; extern const long MAP_LOCKED; extern const long MAP_NONBLOCK; extern const long MAP_NORESERVE; +extern const long MAP_NOSYNC; extern const long MAP_POPULATE; extern const long MAP_PRIVATE; extern const long MAP_SHARED; @@ -26,28 +30,30 @@ extern const long MAP_SHARED; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_STACK 6 -#define MAP_TYPE 15 -#define MAP_FIXED 16 -#define MAP_FIXED_NOREPLACE 0x8000000 +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_STACK 6 +#define MAP_TYPE 15 +#define MAP_FIXED 16 -#define MAP_32BIT SYMBOLIC(MAP_32BIT) -#define MAP_ANONYMOUS SYMBOLIC(MAP_ANONYMOUS) -#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL) -#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL) -#define MAP_DENYWRITE SYMBOLIC(MAP_DENYWRITE) -#define MAP_EXECUTABLE SYMBOLIC(MAP_EXECUTABLE) -#define MAP_GROWSDOWN SYMBOLIC(MAP_GROWSDOWN) -#define MAP_HUGETLB SYMBOLIC(MAP_HUGETLB) -#define MAP_HUGE_MASK SYMBOLIC(MAP_HUGE_MASK) -#define MAP_HUGE_SHIFT SYMBOLIC(MAP_HUGE_SHIFT) -#define MAP_LOCKED SYMBOLIC(MAP_LOCKED) -#define MAP_NONBLOCK SYMBOLIC(MAP_NONBLOCK) -#define MAP_NORESERVE SYMBOLIC(MAP_NORESERVE) -#define MAP_POPULATE SYMBOLIC(MAP_POPULATE) +#define MAP_32BIT SYMBOLIC(MAP_32BIT) +#define MAP_ANONYMOUS SYMBOLIC(MAP_ANONYMOUS) +#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL) +#define MAP_DENYWRITE SYMBOLIC(MAP_DENYWRITE) +#define MAP_EXECUTABLE SYMBOLIC(MAP_EXECUTABLE) +#define MAP_FIXED_NOREPLACE SYMBOLIC(MAP_FIXED_NOREPLACE) +#define MAP_GROWSDOWN SYMBOLIC(MAP_GROWSDOWN) +#define MAP_HASSEMAPHORE SYMBOLIC(MAP_HASSEMAPHORE) +#define MAP_HUGETLB SYMBOLIC(MAP_HUGETLB) +#define MAP_HUGE_MASK SYMBOLIC(MAP_HUGE_MASK) +#define MAP_HUGE_SHIFT SYMBOLIC(MAP_HUGE_SHIFT) +#define MAP_INHERIT SYMBOLIC(MAP_INHERIT) +#define MAP_LOCKED SYMBOLIC(MAP_LOCKED) +#define MAP_NONBLOCK SYMBOLIC(MAP_NONBLOCK) +#define MAP_NORESERVE SYMBOLIC(MAP_NORESERVE) +#define MAP_NOSYNC SYMBOLIC(MAP_NOSYNC) +#define MAP_POPULATE SYMBOLIC(MAP_POPULATE) #define MAP_ANON MAP_ANONYMOUS #define MAP_NOCORE MAP_CONCEAL diff --git a/libc/sysv/consts/ss.h b/libc/sysv/consts/ss.h index 1656da739..a2414c41a 100644 --- a/libc/sysv/consts/ss.h +++ b/libc/sysv/consts/ss.h @@ -3,13 +3,16 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +extern const long SIGSTKSZ; +extern const long MINSIGSTKSZ; extern const long SS_DISABLE; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define SIGSTKSZ STACKSIZE -#define SS_ONSTACK 1 -#define SS_DISABLE SS_DISABLE +#define SIGSTKSZ STACKSIZE +#define MINSIGSTKSZ 32768 +#define SS_ONSTACK 1 +#define SS_DISABLE SS_DISABLE #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SS_H_ */ diff --git a/test/libc/release/test.mk b/test/libc/release/test.mk index ec61a09a3..70982d4f3 100644 --- a/test/libc/release/test.mk +++ b/test/libc/release/test.mk @@ -9,7 +9,15 @@ o/$(MODE)/test/libc/release/cosmopolitan.zip: \ o/$(MODE)/ape/ape-no-modify-self.o \ o/$(MODE)/cosmopolitan.a \ o/$(MODE)/third_party/zip/zip.com - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -qj $@ o/cosmopolitan.h o/$(MODE)/ape/ape.lds o/$(MODE)/libc/crt/crt.o o/$(MODE)/ape/ape.o o/$(MODE)/ape/ape-no-modify-self.o o/$(MODE)/cosmopolitan.a + @$(COMPILE) -AZIP -T$@ \ + o/$(MODE)/third_party/zip/zip.com \ + -qj $@ \ + o/cosmopolitan.h \ + o/$(MODE)/ape/ape.lds \ + o/$(MODE)/libc/crt/crt.o \ + o/$(MODE)/ape/ape.o \ + o/$(MODE)/ape/ape-no-modify-self.o \ + o/$(MODE)/cosmopolitan.a o/$(MODE)/test/libc/release/smoke.com: \ o/$(MODE)/test/libc/release/smoke.com.dbg diff --git a/test/libc/runtime/ape_test.c b/test/libc/runtime/ape_test.c new file mode 100644 index 000000000..9f07cd69f --- /dev/null +++ b/test/libc/runtime/ape_test.c @@ -0,0 +1,107 @@ +/*-*- 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/sigbits.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/errno.h" +#include "libc/mem/io.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/testlib.h" + +STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("apetest.com"); +STATIC_YOINK("apetest2.com"); + +char testlib_enable_tmp_setup_teardown_once; + +void Extract(const char *from, const char *to, int mode) { + ASSERT_SYS(0, 3, open(from, O_RDONLY)); + ASSERT_SYS(0, 4, creat(to, mode)); + ASSERT_NE(-1, _copyfd(3, 4, -1)); + EXPECT_SYS(0, 0, close(4)); + EXPECT_SYS(0, 0, close(3)); +} + +void SetUpOnce(void) { + ASSERT_SYS(0, 0, mkdir("bin", 0755)); + Extract("/zip/apetest.com", "bin/apetest.com", 0755); + Extract("/zip/apetest2.com", "bin/apetest2.com", 0755); + + // force the extraction of ape payload + // if system does not have binfmt_misc + ASSERT_SYS(0, 0, mkdir("tmp", 0755)); + setenv("TMPDIR", "tmp", true); +} + +void RunApeTest(const char *path) { + size_t n; + ssize_t rc, got; + char buf[512] = {0}; + sigset_t chldmask, savemask; + int i, pid, fdin, wstatus, pfds[2]; + struct sigaction ignore, saveint, savequit, savepipe; + ignore.sa_flags = 0; + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); + sigaction(SIGPIPE, &ignore, &savepipe); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); + ASSERT_SYS(0, 0, pipe2(pfds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + __strace = 0; + __ftrace = 0; + close(1); + dup(pfds[1]); + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigaction(SIGPIPE, &savepipe, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + execv(path, (char *const[]){path, 0}); + _Exit(127); + } + close(pfds[1]); + EXPECT_SYS(0, 8, read(pfds[0], buf, sizeof(buf))); + EXPECT_STREQ("success\n", buf); + close(pfds[0]); + EXPECT_NE(-1, wait(&wstatus)); + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigaction(SIGPIPE, &savepipe, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + EXPECT_SYS(0, 3, open(path, O_RDONLY)); + EXPECT_SYS(0, 6, read(3, buf, 6)); + EXPECT_SYS(0, 0, close(3)); + EXPECT_STREQN("MZqFpD", buf, 6); +} + +TEST(apeNoModifySelf, runsWithoutModifyingSelf) { + RunApeTest("bin/apetest.com"); +} + +TEST(apeCopySelf, runsWithoutModifyingSelf) { + RunApeTest("bin/apetest2.com"); +} diff --git a/test/libc/runtime/test.mk b/test/libc/runtime/test.mk index 2b1b75b95..c4881f68a 100644 --- a/test/libc/runtime/test.mk +++ b/test/libc/runtime/test.mk @@ -41,6 +41,8 @@ TEST_LIBC_RUNTIME_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ + LIBC_ZIPOS \ + TOOL_BUILD_LIB \ THIRD_PARTY_XED TEST_LIBC_RUNTIME_DEPS := \ @@ -59,6 +61,15 @@ o/$(MODE)/test/libc/runtime/%.com.dbg: \ $(APE) @$(APELINK) +o/$(MODE)/test/libc/runtime/ape_test.com.dbg: \ + $(TEST_LIBC_RUNTIME_DEPS) \ + o/$(MODE)/test/libc/runtime/ape_test.o \ + o/$(MODE)/test/libc/runtime/runtime.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE) + @$(APELINK) + $(TEST_LIBC_RUNTIME_OBJS): \ DEFAULT_CCFLAGS += \ -fno-builtin @@ -67,7 +78,6 @@ o/$(MODE)/test/libc/runtime/getenv_test.com.runs: \ o/$(MODE)/test/libc/runtime/getenv_test.com @HELLO=THERE build/runit $@ $< -o/$(MODE)/test/libc/runtime/fun_test.o \ o/$(MODE)/test/libc/runtime/itsatrap_test.o: \ OVERRIDE_CFLAGS += \ -fno-sanitize=all \ diff --git a/test/tool/plinko/plinko_test.c b/test/tool/plinko/plinko_test.c index f9f11a036..b4349fd1b 100644 --- a/test/tool/plinko/plinko_test.c +++ b/test/tool/plinko/plinko_test.c @@ -72,13 +72,13 @@ TEST(plinko, worksOrPrintsNiceError) { struct sigaction ignore, saveint, savequit, savepipe; ignore.sa_flags = 0; ignore.sa_handler = SIG_IGN; - EXPECT_EQ(0, sigemptyset(&ignore.sa_mask)); - EXPECT_EQ(0, sigaction(SIGINT, &ignore, &saveint)); - EXPECT_EQ(0, sigaction(SIGQUIT, &ignore, &savequit)); - EXPECT_EQ(0, sigaction(SIGPIPE, &ignore, &savepipe)); - EXPECT_EQ(0, sigemptyset(&chldmask)); - EXPECT_EQ(0, sigaddset(&chldmask, SIGCHLD)); - EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); + sigaction(SIGPIPE, &ignore, &savepipe); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); ASSERT_NE(-1, pipe2(pfds[0], O_CLOEXEC)); ASSERT_NE(-1, pipe2(pfds[1], O_CLOEXEC)); ASSERT_NE(-1, (pid = fork())); @@ -90,14 +90,14 @@ TEST(plinko, worksOrPrintsNiceError) { close(2), dup(pfds[1][1]); sigaction(SIGINT, &saveint, 0); sigaction(SIGQUIT, &savequit, 0); - sigaction(SIGQUIT, &savepipe, 0); + sigaction(SIGPIPE, &savepipe, 0); sigprocmask(SIG_SETMASK, &savemask, 0); execve("bin/plinko.com", (char *const[]){"bin/plinko.com", 0}, (char *const[]){0}); _exit(127); } - EXPECT_NE(-1, close(pfds[0][0])); - EXPECT_NE(-1, close(pfds[1][1])); + close(pfds[0][0]); + close(pfds[1][1]); for (i = 0; i < ARRAYLEN(kSauces); ++i) { EXPECT_NE(-1, (fdin = open(kSauces[i], O_RDONLY))); rc = _copyfd(fdin, pfds[0][1], -1); @@ -118,10 +118,10 @@ TEST(plinko, worksOrPrintsNiceError) { } else { EXPECT_EQ(1, WEXITSTATUS(wstatus)); } - EXPECT_EQ(0, sigaction(SIGINT, &saveint, 0)); - EXPECT_EQ(0, sigaction(SIGQUIT, &savequit, 0)); - EXPECT_EQ(0, sigaction(SIGPIPE, &savepipe, 0)); - EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &savemask, 0)); + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigaction(SIGPIPE, &savepipe, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); if (g_testlib_failed) { kprintf("note: got the following in pipe: %s%n", buf); } diff --git a/tool/build/lib/apetest.c b/tool/build/lib/apetest.c new file mode 100644 index 000000000..99e91c535 --- /dev/null +++ b/tool/build/lib/apetest.c @@ -0,0 +1,23 @@ +/*-*- 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" + +int main(int argc, char *argv[]) { + write(1, "success\n", 8); +} diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index 383200cab..06b287a6d 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -9,7 +9,9 @@ TOOL_BUILD_LIB_A = o/$(MODE)/tool/build/lib/buildlib.a TOOL_BUILD_LIB_A_FILES := $(wildcard tool/build/lib/*) TOOL_BUILD_LIB_A_HDRS = $(filter %.h,$(TOOL_BUILD_LIB_A_FILES)) TOOL_BUILD_LIB_A_SRCS_S = $(filter %.S,$(TOOL_BUILD_LIB_A_FILES)) -TOOL_BUILD_LIB_A_SRCS_C = $(filter %.c,$(TOOL_BUILD_LIB_A_FILES)) + +TOOL_BUILD_LIB_A_SRCS_C = \ + $(filter-out tool/build/lib/apetest.c,$(filter %.c,$(TOOL_BUILD_LIB_A_FILES))) TOOL_BUILD_LIB_A_CHECKS = \ $(TOOL_BUILD_LIB_A_HDRS:%=o/$(MODE)/%.ok) \ @@ -19,9 +21,15 @@ TOOL_BUILD_LIB_A_SRCS = \ $(TOOL_BUILD_LIB_A_SRCS_S) \ $(TOOL_BUILD_LIB_A_SRCS_C) +TOOL_BUILD_LIB_COMS = \ + o/$(MODE)/tool/build/lib/apetest.com \ + o/$(MODE)/tool/build/lib/apetest2.com + TOOL_BUILD_LIB_A_OBJS = \ $(TOOL_BUILD_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \ - $(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o) + $(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + o/$(MODE)/tool/build/lib/apetest.com.zip.o \ + o/$(MODE)/tool/build/lib/apetest2.com.zip.o TOOL_BUILD_LIB_A_DIRECTDEPS = \ LIBC_ALG \ @@ -67,6 +75,25 @@ o/$(MODE)/tool/build/lib/ssefloat.o: \ TARGET_ARCH += \ -msse3 +o/$(MODE)/tool/build/lib/apetest.com.dbg: \ + $(TOOL_BUILD_LIB_A_DEPS) \ + o/$(MODE)/tool/build/lib/apetest.o \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/tool/build/lib/apetest2.com.dbg: \ + $(TOOL_BUILD_LIB_A_DEPS) \ + o/$(MODE)/tool/build/lib/apetest.o \ + $(CRT) \ + $(APE_COPY_SELF) + @$(APELINK) + +o/$(MODE)/tool/build/lib/apetest.com.zip.o \ +o/$(MODE)/tool/build/lib/apetest2.com.zip.o: \ + ZIPOBJ_FLAGS += \ + -B + TOOL_BUILD_LIB_LIBS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x))) TOOL_BUILD_LIB_SRCS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_SRCS)) TOOL_BUILD_LIB_HDRS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_HDRS)) @@ -76,4 +103,6 @@ TOOL_BUILD_LIB_OBJS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_OBJS)) TOOL_BUILD_LIB_TESTS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_TESTS)) .PHONY: o/$(MODE)/tool/build/lib -o/$(MODE)/tool/build/lib: $(TOOL_BUILD_LIB_CHECKS) +o/$(MODE)/tool/build/lib: \ + $(TOOL_BUILD_LIB_COMS) \ + $(TOOL_BUILD_LIB_CHECKS) diff --git a/tool/net/redbean.c b/tool/net/redbean.c index ee5f1a3a0..5b60d7d1f 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -7305,7 +7305,6 @@ void RedBean(int argc, char *argv[]) { #else GetHostsTxt(); // for effect GetResolvConf(); // for effect - __print_maps(); if (daemonize || uniprocess || !linenoiseIsTerminal()) { EventLoop(HEARTBEAT); } else if (IsWindows()) { From 1f229e4efc9affff3114ca5615d62e7c4503c0a0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 22 May 2022 08:13:13 -0700 Subject: [PATCH 31/40] Use re-entrant locks on stdio --- libc/calls/interrupts-nt.c | 19 ++++---- libc/intrin/gettid.greg.c | 6 +++ libc/intrin/spinlock.h | 2 +- libc/log/log.h | 4 +- libc/log/vflogf.c | 41 +++++++++-------- libc/mem/aligned_alloc.c | 1 + libc/mem/asprintf.c | 1 + libc/mem/calloc.S | 1 + libc/mem/free.S | 1 + libc/mem/get_current_dir_name.c | 1 + libc/mem/malloc.S | 1 + libc/mem/malloc_usable_size.S | 1 + libc/mem/memalign.S | 1 + libc/mem/posix_memalign.c | 1 + libc/mem/pvalloc.c | 1 + libc/mem/realloc.S | 1 + libc/mem/realloc_in_place.S | 1 + libc/mem/reallocarray.c | 1 + libc/mem/strdup.c | 1 + libc/mem/strndup.c | 1 + libc/mem/valloc.c | 1 + libc/mem/vasprintf.c | 1 + libc/mem/wcsdup.c | 1 + libc/runtime/ftracer.c | 3 +- libc/stdio/fclose.c | 1 - libc/stdio/fflush.c | 2 +- libc/stdio/flockfile.c | 22 +++++++-- libc/stdio/flushlbf.c | 4 +- libc/stdio/fprintf.c | 8 +++- libc/stdio/fprintf_unlocked.c | 32 +++++++++++++ libc/stdio/freopen.c | 5 +- libc/stdio/fseeko.c | 5 +- libc/stdio/ftello.c | 5 +- libc/stdio/ftrylockfile.c | 18 ++++++-- libc/stdio/getdelim.c | 5 +- libc/stdio/printf.c | 2 +- libc/stdio/puts.c | 29 ++++++++---- libc/stdio/rewind.c | 7 ++- libc/stdio/setvbuf.c | 2 + libc/stdio/stdio.h | 57 +++++++++++++---------- libc/stdio/unlocked/clearerr_unlocked.S | 1 + libc/stdio/unlocked/feof_unlocked.S | 1 + libc/stdio/unlocked/ferror_unlocked.S | 1 + libc/stdio/unlocked/fflush_unlocked.S | 10 ++-- libc/stdio/unlocked/fgetc_unlocked.S | 1 + libc/stdio/unlocked/fgets_unlocked.S | 1 + libc/stdio/unlocked/fgetwc_unlocked.S | 1 + libc/stdio/unlocked/fgetws_unlocked.S | 1 + libc/stdio/unlocked/fileno_unlocked.S | 1 + libc/stdio/unlocked/fputc_unlocked.S | 1 + libc/stdio/unlocked/fputs_unlocked.S | 1 + libc/stdio/unlocked/fputwc_unlocked.S | 1 + libc/stdio/unlocked/fputws_unlocked.S | 1 + libc/stdio/unlocked/fread_unlocked.S | 1 + libc/stdio/unlocked/fseeko_unlocked.S | 37 +++++++++++++++ libc/stdio/unlocked/fwrite_unlocked.S | 1 + libc/stdio/unlocked/getc_unlocked.S | 1 + libc/stdio/unlocked/getchar_unlocked.S | 1 + libc/stdio/unlocked/getwc_unlocked.S | 1 + libc/stdio/unlocked/getwchar_unlocked.S | 1 + libc/stdio/unlocked/putc_unlocked.S | 1 + libc/stdio/unlocked/putchar_unlocked.S | 1 + libc/stdio/unlocked/putwc_unlocked.S | 1 + libc/stdio/unlocked/putwchar_unlocked.S | 1 + libc/stdio/unlocked/stdio_unlock.S | 43 ++++++++--------- libc/stdio/unlocked/ungetc_unlocked.S | 1 + libc/stdio/unlocked/ungetwc_unlocked.S | 1 + libc/stdio/vfprintf.c | 47 ++++--------------- libc/stdio/vfprintf_unlocked.c | 61 +++++++++++++++++++++++++ libc/stdio/vprintf.c | 4 ++ test/libc/stdio/fputc_test.c | 11 +++++ third_party/mbedtls/ssl.h | 14 ------ tool/build/lib/eztls.c | 1 + tool/build/runit.c | 35 ++++++++++---- tool/build/runitd.c | 19 +++++++- tool/emacs/cosmo-asm-mode.el | 1 + tool/net/redbean.c | 1 + tool/net/wb.c | 1 + 78 files changed, 427 insertions(+), 179 deletions(-) create mode 100644 libc/stdio/fprintf_unlocked.c create mode 100644 libc/stdio/unlocked/fseeko_unlocked.S create mode 100644 libc/stdio/vfprintf_unlocked.c diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index eda802dab..1e103539b 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -28,15 +28,18 @@ _Alignas(64) static int rlock; -static privileged inline bool AcquireInterruptPollLock(void) { +// return 0 on success, or tid of other owner +static privileged inline int AcquireInterruptPollLock(void) { // any thread can poll for interrupts // but it's wasteful to have every single thread doing it - int me, owner, tries; - if (!__threaded) return true; - me = gettid(); - owner = 0; - if (_lockcmpxchgp(&rlock, &owner, me)) return true; - return owner == me; + int me, owner = 0; + if (__threaded) { + me = gettid(); + if (!_lockcmpxchgp(&rlock, &owner, me) && owner == me) { + owner = 0; + } + } + return owner; } static textwindows inline void ReleaseInterruptPollLock(void) { @@ -47,7 +50,7 @@ static textwindows inline void ReleaseInterruptPollLock(void) { textwindows bool _check_interrupts(bool restartable, struct Fd *fd) { bool res; if (__time_critical) return false; - if (!AcquireInterruptPollLock()) return false; + if (AcquireInterruptPollLock()) return false; if (weaken(_check_sigalrm)) weaken(_check_sigalrm)(); if (weaken(_check_sigchld)) weaken(_check_sigchld)(); if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd); diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index e9950b359..4cc21ca3f 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -27,6 +27,12 @@ __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; /** * Returns current thread id. + * + * On Linux, and Linux only, this is guaranteed to be equal to getpid() + * if this is the main thread. On NetBSD, gettid() for the main thread + * is always 1. + * + * @return thread id greater than zero or -1 w/ errno * @asyncsignalsafe */ privileged int gettid(void) { diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index 50b2761ad..b1df648dc 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -64,7 +64,7 @@ do { \ autotype(lock) __lock = (lock); \ typeof(*__lock) __x; \ - int __tries = 0; \ + unsigned __tries = 0; \ for (;;) { \ __atomic_load(__lock, &__x, __ATOMIC_RELAXED); \ if (!__x && !_trylock_inline(__lock)) { \ diff --git a/libc/log/log.h b/libc/log/log.h index 18be04844..c026b5982 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -7,6 +7,7 @@ #include "libc/calls/struct/winsize.h" #include "libc/errno.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" /*───────────────────────────────────────────────────────────────────────────│─╗ @@ -89,7 +90,8 @@ extern unsigned __log_level; /* log level for runtime check */ --__ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ if (weaken(__die)) weaken(__die)(); \ - exit(1); \ + __restorewintty(); \ + _Exit(1); \ unreachable; \ } while (0) diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 0f07c5f6e..c67a7d8ff 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -41,24 +41,25 @@ #define kNontrivialSize (8 * 1000 * 1000) static struct timespec vflogf_ts; -_Alignas(64) static int vflogf_lock; /** * Takes corrective action if logging is on the fritz. */ -void vflogf_onfail(FILE *f) { +static void vflogf_onfail(FILE *f) { errno_t err; int64_t size; if (IsTiny()) return; - err = ferror(f); - if (fileno(f) != -1 && (err == ENOSPC || err == EDQUOT || err == EFBIG) && - ((size = getfiledescriptorsize(fileno(f))) == -1 || + err = ferror_unlocked(f); + if (fileno_unlocked(f) != -1 && + (err == ENOSPC || err == EDQUOT || err == EFBIG) && + ((size = getfiledescriptorsize(fileno_unlocked(f))) == -1 || size > kNontrivialSize)) { - ftruncate(fileno(f), 0); - fseek(f, SEEK_SET, 0); + ftruncate(fileno_unlocked(f), 0); + fseeko_unlocked(f, SEEK_SET, 0); f->beg = f->end = 0; - clearerr(f); - (fprintf)(f, "performed emergency log truncation: %s\n", strerror(err)); + clearerr_unlocked(f); + (fprintf_unlocked)(f, "performed emergency log truncation: %s\n", + strerror(err)); } } @@ -88,8 +89,8 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, int64_t secs, nsec, dots; if (!f) f = __log_file; if (!f) return; - _spinlock(&vflogf_lock); - __atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED); + flockfile(f); + --__strace; t2 = nowl(); secs = t2; @@ -105,16 +106,17 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, bufmode = f->bufmode; if (bufmode == _IOLBF) f->bufmode = _IOFBF; - if ((fprintf)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32, - rem1000000int64(div1000int64(dots)), file, line, - strchrnul(prog, '.') - prog, prog, getpid()) <= 0) { + if ((fprintf_unlocked)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", + "FEWIVDNT"[level & 7], buf32, + rem1000000int64(div1000int64(dots)), file, line, + strchrnul(prog, '.') - prog, prog, getpid()) <= 0) { vflogf_onfail(f); } - (vfprintf)(f, fmt, va); - fprintf(f, "\n"); + (vfprintf_unlocked)(f, fmt, va); + fputc_unlocked('\n', f); if (bufmode == _IOLBF) { f->bufmode = _IOLBF; - fflush(f); + fflush_unlocked(f); } if (level == kLogFatal) { @@ -122,11 +124,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, strcpy(buf32, "unknown"); gethostname(buf32, sizeof(buf32)); (dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid()); - _spunlock(&vflogf_lock); __die(); unreachable; } - __atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED); - _spunlock(&vflogf_lock); + ++__strace; + funlockfile(f); } diff --git a/libc/mem/aligned_alloc.c b/libc/mem/aligned_alloc.c index fc2133098..6c734f313 100644 --- a/libc/mem/aligned_alloc.c +++ b/libc/mem/aligned_alloc.c @@ -27,6 +27,7 @@ * @return memory address, or NULL w/ errno * @throw EINVAL if !IS2POW(a) * @see pvalloc() + * @threadsafe */ void *aligned_alloc(size_t a, size_t n) { if (IS2POW(a)) { diff --git a/libc/mem/asprintf.c b/libc/mem/asprintf.c index 5d20342d0..7e76a84d0 100644 --- a/libc/mem/asprintf.c +++ b/libc/mem/asprintf.c @@ -26,6 +26,7 @@ * portability, since that's guaranteed to work with all libraries * @return bytes written (excluding NUL) or -1 w/ errno * @see xasprintf() for a better API + * @threadsafe */ int(asprintf)(char **strp, const char *fmt, ...) { int res; diff --git a/libc/mem/calloc.S b/libc/mem/calloc.S index 8f89690ef..0486dfac1 100644 --- a/libc/mem/calloc.S +++ b/libc/mem/calloc.S @@ -26,5 +26,6 @@ // @return rax is memory address, or NULL w/ errno // @note overreliance on memalign is a sure way to fragment space // @see dlcalloc() +// @threadsafe calloc: jmp *hook_calloc(%rip) .endfn calloc,globl diff --git a/libc/mem/free.S b/libc/mem/free.S index 0190fe07c..35a7cc937 100644 --- a/libc/mem/free.S +++ b/libc/mem/free.S @@ -28,5 +28,6 @@ // // @param rdi is allocation address, which may be NULL // @see dlfree() +// @threadsafe free: jmp *hook_free(%rip) .endfn free,globl diff --git a/libc/mem/get_current_dir_name.c b/libc/mem/get_current_dir_name.c index 403bffb0c..0f68b0e53 100644 --- a/libc/mem/get_current_dir_name.c +++ b/libc/mem/get_current_dir_name.c @@ -28,6 +28,7 @@ * that'll be returned. * * @return pointer that must be free()'d, or NULL w/ errno + * @threadsafe */ dontdiscard char *get_current_dir_name(void) { const char *p; diff --git a/libc/mem/malloc.S b/libc/mem/malloc.S index 3e7388c66..1afda752c 100644 --- a/libc/mem/malloc.S +++ b/libc/mem/malloc.S @@ -33,5 +33,6 @@ // // @param rdi is number of bytes needed, coerced to 1+ // @return new memory, or NULL w/ errno +// @threadsafe malloc: jmp *hook_malloc(%rip) .endfn malloc,globl diff --git a/libc/mem/malloc_usable_size.S b/libc/mem/malloc_usable_size.S index 0242a7708..161f050fd 100644 --- a/libc/mem/malloc_usable_size.S +++ b/libc/mem/malloc_usable_size.S @@ -35,6 +35,7 @@ // @param rdi is address of allocation // @return rax is total number of bytes // @see dlmalloc_usable_size() +// @threadsafe malloc_usable_size: jmp *hook_malloc_usable_size(%rip) .endfn malloc_usable_size,globl diff --git a/libc/mem/memalign.S b/libc/mem/memalign.S index 9c75364f6..9a8e762ee 100644 --- a/libc/mem/memalign.S +++ b/libc/mem/memalign.S @@ -30,6 +30,7 @@ // @param rsi is number of bytes needed, coerced to 1+ // @return rax is memory address, or NULL w/ errno // @see valloc(), pvalloc() +// @threadsafe memalign: jmp *hook_memalign(%rip) .endfn memalign,globl diff --git a/libc/mem/posix_memalign.c b/libc/mem/posix_memalign.c index a4c173773..3b384ba21 100644 --- a/libc/mem/posix_memalign.c +++ b/libc/mem/posix_memalign.c @@ -35,6 +35,7 @@ * @param bytes is number of bytes to allocate * @return return 0 or EINVAL or ENOMEM w/o setting errno * @see memalign() + * @threadsafe */ int posix_memalign(void **pp, size_t alignment, size_t bytes) { int e; diff --git a/libc/mem/pvalloc.c b/libc/mem/pvalloc.c index 1c59640ad..2fb40aea2 100644 --- a/libc/mem/pvalloc.c +++ b/libc/mem/pvalloc.c @@ -25,6 +25,7 @@ * @param n number of bytes needed * @return memory address, or NULL w/ errno * @see valloc() + * @threadsafe */ void *pvalloc(size_t n) { return memalign(PAGESIZE, ROUNDUP(n, PAGESIZE)); diff --git a/libc/mem/realloc.S b/libc/mem/realloc.S index 01f398c1e..b89010ff8 100644 --- a/libc/mem/realloc.S +++ b/libc/mem/realloc.S @@ -53,6 +53,7 @@ // @note realloc(p=0, n=0) → malloc(32) // @note realloc(p≠0, n=0) → free(p) // @see dlrealloc() +// @threadsafe realloc: jmp *hook_realloc(%rip) .endfn realloc,globl diff --git a/libc/mem/realloc_in_place.S b/libc/mem/realloc_in_place.S index 5ad041b76..674393545 100644 --- a/libc/mem/realloc_in_place.S +++ b/libc/mem/realloc_in_place.S @@ -32,6 +32,7 @@ // @param rsi (newsize) is number of bytes needed // @return rax is result, or NULL w/ errno // @see dlrealloc_in_place() +// @threadsafe realloc_in_place: jmp *hook_realloc_in_place(%rip) .endfn realloc_in_place,globl diff --git a/libc/mem/reallocarray.c b/libc/mem/reallocarray.c index 0c1c5750b..c6f2bc548 100644 --- a/libc/mem/reallocarray.c +++ b/libc/mem/reallocarray.c @@ -26,6 +26,7 @@ * @param ptr may be NULL for malloc() behavior * @param nmemb may be 0 for free() behavior; shrinking is promised too * @return new address or NULL w/ errno and ptr is NOT free()'d + * @threadsafe */ void *reallocarray(void *ptr, size_t nmemb, size_t itemsize) { size_t n; diff --git a/libc/mem/strdup.c b/libc/mem/strdup.c index f3fe00ce3..17cb55afb 100644 --- a/libc/mem/strdup.c +++ b/libc/mem/strdup.c @@ -25,6 +25,7 @@ * @param s is a NUL-terminated byte string * @return new string or NULL w/ errno * @error ENOMEM + * @threadsafe */ char *strdup(const char *s) { size_t len = strlen(s); diff --git a/libc/mem/strndup.c b/libc/mem/strndup.c index a4facf3b4..382b57da1 100644 --- a/libc/mem/strndup.c +++ b/libc/mem/strndup.c @@ -26,6 +26,7 @@ * @param n if less than strlen(s) will truncate the string * @return new string or NULL w/ errno * @error ENOMEM + * @threadsafe */ char *strndup(const char *s, size_t n) { char *s2; diff --git a/libc/mem/valloc.c b/libc/mem/valloc.c index 39467eaeb..3f8e0fc77 100644 --- a/libc/mem/valloc.c +++ b/libc/mem/valloc.c @@ -24,6 +24,7 @@ * @param n number of bytes needed * @return memory address, or NULL w/ errno * @see pvalloc() + * @threadsafe */ void *valloc(size_t n) { return memalign(PAGESIZE, n); diff --git a/libc/mem/vasprintf.c b/libc/mem/vasprintf.c index 26ad2fe6c..b002c8a34 100644 --- a/libc/mem/vasprintf.c +++ b/libc/mem/vasprintf.c @@ -23,6 +23,7 @@ /** * Formats string w/ dynamic memory allocation. * @see xasprintf() for a better API + * @threadsafe */ int(vasprintf)(char **strp, const char *fmt, va_list va) { va_list vb; diff --git a/libc/mem/wcsdup.c b/libc/mem/wcsdup.c index c143f7a3c..97e5a8751 100644 --- a/libc/mem/wcsdup.c +++ b/libc/mem/wcsdup.c @@ -21,6 +21,7 @@ /** * Allocates copy of wide string. + * @threadsafe */ wchar_t *wcsdup(const wchar_t *s) { size_t len = wcslen(s); diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index ae0359265..e4f16be5e 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -70,7 +70,8 @@ static privileged inline void ReleaseFtraceLock(void) { } static privileged inline bool AcquireFtraceLock(void) { - int me, owner, tries; + int me, owner; + unsigned tries; if (!__threaded) { return _cmpxchg(&ftrace_lock, 0, -1); } else { diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c index 94b5aebe6..cd75a500e 100644 --- a/libc/stdio/fclose.c +++ b/libc/stdio/fclose.c @@ -19,7 +19,6 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c index 28f9f18e6..b9411a5bc 100644 --- a/libc/stdio/fflush.c +++ b/libc/stdio/fflush.c @@ -33,7 +33,7 @@ /** * Blocks until data from stream buffer is written out. * - * @param f is the stream handle + * @param f is the stream handle, or 0 for all streams * @return is 0 on success or -1 on error */ int fflush_unlocked(FILE *f) { diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c index 5ffe0c691..223ead118 100644 --- a/libc/stdio/flockfile.c +++ b/libc/stdio/flockfile.c @@ -16,12 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/spinlock.h" +#include "libc/calls/calls.h" +#include "libc/intrin/cmpxchg.h" +#include "libc/intrin/lockcmpxchgp.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" /** - * Acquires lock on stdio object, blocking if needed. + * Acquires reentrant lock on stdio object, blocking if needed. */ void flockfile(FILE *f) { - _spinlock(&f->lock); + int me, owner; + unsigned tries; + if (!__threaded) return; + for (tries = 0, me = gettid();;) { + owner = 0; + if (_lockcmpxchgp(&f->lock, &owner, me) || owner == me) { + return; + } + if (++tries & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } + } } diff --git a/libc/stdio/flushlbf.c b/libc/stdio/flushlbf.c index bacc56caa..e4b40a07a 100644 --- a/libc/stdio/flushlbf.c +++ b/libc/stdio/flushlbf.c @@ -31,11 +31,11 @@ void _flushlbf(void) { _spinlock(&__fflush.lock); for (i = 0; i < __fflush.handles.i; ++i) { if ((f = __fflush.handles.p[i])) { - _spinlock(&f->lock); + flockfile(f); if (f->bufmode == _IOLBF) { fflush_unlocked(f); } - _spunlock(&f->lock); + funlockfile(f); } } _spunlock(&__fflush.lock); diff --git a/libc/stdio/fprintf.c b/libc/stdio/fprintf.c index 10223fb6c..939a6cd69 100644 --- a/libc/stdio/fprintf.c +++ b/libc/stdio/fprintf.c @@ -18,11 +18,17 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/** + * Formats and writes text to stream. + * @see printf() for further documentation + */ int(fprintf)(FILE *f, const char *fmt, ...) { int rc; va_list va; + flockfile(f); va_start(va, fmt); - rc = (vfprintf)(f, fmt, va); + rc = (vfprintf_unlocked)(f, fmt, va); va_end(va); + funlockfile(f); return rc; } diff --git a/libc/stdio/fprintf_unlocked.c b/libc/stdio/fprintf_unlocked.c new file mode 100644 index 000000000..f6267dab2 --- /dev/null +++ b/libc/stdio/fprintf_unlocked.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=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/stdio/stdio.h" + +/** + * Formats and writes text to stream. + * @see printf() for further documentation + */ +int(fprintf_unlocked)(FILE *f, const char *fmt, ...) { + int rc; + va_list va; + va_start(va, fmt); + rc = (vfprintf_unlocked)(f, fmt, va); + va_end(va); + return rc; +} diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c index 4d5a4218c..1099e79b8 100644 --- a/libc/stdio/freopen.c +++ b/libc/stdio/freopen.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/intrin/spinlock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" @@ -41,7 +40,7 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) { FILE *res; unsigned flags; flags = fopenflags(mode); - _spinlock(&stream->lock); + flockfile(stream); fflush_unlocked(stream); if (pathname) { /* open new stream, overwriting existing alloc */ @@ -60,6 +59,6 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) { fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC); res = stream; } - _spunlock(&stream->lock); + funlockfile(stream); return res; } diff --git a/libc/stdio/fseeko.c b/libc/stdio/fseeko.c index 2248dffae..0a7c08bdb 100644 --- a/libc/stdio/fseeko.c +++ b/libc/stdio/fseeko.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/intrin/spinlock.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" @@ -36,11 +35,10 @@ * @param whence can be SEET_SET, SEEK_CUR, or SEEK_END * @returns 0 on success or -1 on error */ -int fseeko(FILE *f, int64_t offset, int whence) { +int fseeko_unlocked(FILE *f, int64_t offset, int whence) { int res; ssize_t rc; int64_t pos; - _spinlock(&f->lock); if (f->fd != -1) { if (__fflush_impl(f) == -1) return -1; if (whence == SEEK_CUR && f->beg < f->end) { @@ -77,6 +75,5 @@ int fseeko(FILE *f, int64_t offset, int whence) { res = -1; } } - _spunlock(&f->lock); return res; } diff --git a/libc/stdio/ftello.c b/libc/stdio/ftello.c index d3b88c846..9e5f97b48 100644 --- a/libc/stdio/ftello.c +++ b/libc/stdio/ftello.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/intrin/spinlock.h" #include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" @@ -49,8 +48,8 @@ static int64_t ftello_unlocked(FILE *f) { */ int64_t ftello(FILE *f) { int64_t rc; - _spinlock(&f->lock); + flockfile(f); rc = ftello_unlocked(f); - _spunlock(&f->lock); + funlockfile(f); return rc; } diff --git a/libc/stdio/ftrylockfile.c b/libc/stdio/ftrylockfile.c index 97d21da2f..20bffd3ca 100644 --- a/libc/stdio/ftrylockfile.c +++ b/libc/stdio/ftrylockfile.c @@ -16,13 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/spinlock.h" +#include "libc/calls/calls.h" +#include "libc/intrin/lockcmpxchgp.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" /** - * Tries to acquire stdio object lock. - * @return 0 for success or non-zero if someone else has the lock + * Tries to acquire reentrant stdio object lock. + * + * @return 0 on success, or non-zero if another thread owns the lock */ int ftrylockfile(FILE *f) { - return _trylock(&f->lock); + int me, owner = 0; + if (__threaded) { + me = gettid(); + if (!_lockcmpxchgp(&f->lock, &owner, me) && owner == me) { + owner = 0; + } + } + return owner; } diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index ae2a91f95..fa29af049 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -19,7 +19,6 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -87,8 +86,8 @@ static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) { */ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) { ssize_t rc; - _spinlock(&f->lock); + flockfile(f); rc = getdelim_unlocked(s, n, delim, f); - _spunlock(&f->lock); + funlockfile(f); return rc; } diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index 5dcffa13a..bfd4acf3a 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -19,7 +19,7 @@ #include "libc/stdio/stdio.h" /** - * Formats and writes string to stdout. + * Formats and writes text to stdout. * * Cosmopolitan supports most of the standard formatting behaviors * described by `man 3 printf`, in addition to the following diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c index 044420e20..fcd0280a2 100644 --- a/libc/stdio/puts.c +++ b/libc/stdio/puts.c @@ -18,21 +18,32 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" -/** - * Writes string w/ trailing newline to stdout. - */ -int puts(const char *s) { - FILE *f; +static int PutsImpl(const char *s, FILE *f) { size_t n, r; - f = stdout; if ((n = strlen(s))) { - r = fwrite(s, 1, n, f); + r = fwrite_unlocked(s, 1, n, f); if (!r) return -1; if (r < n) return r; } - if (fputc('\n', f) == -1) { - if (feof(f)) return n; + if (fputc_unlocked('\n', f) == -1) { + if (feof_unlocked(f)) return n; return -1; } return n + 1; } + +/** + * Writes string w/ trailing newline to stdout. + * + * @return non-negative number on success, or `EOF` on error with + * `errno` set and the `ferror(stdout)` state is updated + */ +int puts(const char *s) { + FILE *f; + int bytes; + f = stdout; + flockfile(f); + bytes = PutsImpl(s, f); + funlockfile(f); + return bytes; +} diff --git a/libc/stdio/rewind.c b/libc/stdio/rewind.c index 03aa3f5c4..8f655d960 100644 --- a/libc/stdio/rewind.c +++ b/libc/stdio/rewind.c @@ -26,6 +26,9 @@ * EOF state, without reopening it. */ void rewind(FILE *f) { - fseek(f, 0, SEEK_SET); - f->state = 0; + flockfile(f); + if (!fseeko_unlocked(f, 0, SEEK_SET)) { + f->state = 0; + } + funlockfile(f); } diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c index 8cba7b9d4..d6ba56932 100644 --- a/libc/stdio/setvbuf.c +++ b/libc/stdio/setvbuf.c @@ -30,6 +30,7 @@ * @return 0 on success or -1 on error */ int setvbuf(FILE *f, char *buf, int mode, size_t size) { + flockfile(f); if (buf) { if (!size) size = BUFSIZ; if (!f->nofree && f->buf != buf) free_s(&f->buf); @@ -38,5 +39,6 @@ int setvbuf(FILE *f, char *buf, int mode, size_t size) { f->nofree = true; } f->bufmode = mode; + funlockfile(f); return 0; } diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 2eccd28a6..9a903e999 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -116,30 +116,6 @@ int wprintf(const wchar_t *, ...); int wscanf(const wchar_t *, ...); int fwide(FILE *, int); -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § standard i/o » optimizations ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define getc(f) fgetc(f) -#define getwc(f) fgetwc(f) -#define putc(c, f) fputc(c, f) -#define putwc(c, f) fputwc(c, f) - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__) -#define vprintf(FMT, VA) (vprintf)(PFLINK(FMT), VA) -#define fprintf(F, FMT, ...) (fprintf)(F, PFLINK(FMT), ##__VA_ARGS__) -#define vfprintf(F, FMT, VA) (vfprintf)(F, PFLINK(FMT), VA) -#define vscanf(FMT, VA) (vscanf)(SFLINK(FMT), VA) -#define scanf(FMT, ...) (scanf)(SFLINK(FMT), ##__VA_ARGS__) -#define fscanf(F, FMT, ...) (fscanf)(F, SFLINK(FMT), ##__VA_ARGS__) -#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA) -#endif - -#define stdin SYMBOLIC(stdin) -#define stdout SYMBOLIC(stdout) -#define stderr SYMBOLIC(stderr) - /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § standard i/o » without mutexes ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ @@ -172,12 +148,45 @@ wchar_t *fgetws_unlocked(wchar_t *, int, FILE *); int fputws_unlocked(const wchar_t *, FILE *); wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull(); int ungetc_unlocked(int, FILE *) paramsnonnull(); +int fseeko_unlocked(FILE *, int64_t, int) paramsnonnull(); +int fprintf_unlocked(FILE *, const char *, ...) printfesque(2) + paramsnonnull((1, 2)) dontthrow nocallback; +int vfprintf_unlocked(FILE *, const char *, va_list) + paramsnonnull() dontthrow nocallback; #define getc_unlocked(f) fgetc_unlocked(f) #define getwc_unlocked(f) fgetwc_unlocked(f) #define putc_unlocked(c, f) fputc_unlocked(c, f) #define putwc_unlocked(c, f) fputwc_unlocked(c, f) +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § standard i/o » optimizations ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define getc(f) fgetc(f) +#define getwc(f) fgetwc(f) +#define putc(c, f) fputc(c, f) +#define putwc(c, f) fputwc(c, f) + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* clang-format off */ +#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__) +#define vprintf(FMT, VA) (vprintf)(PFLINK(FMT), VA) +#define fprintf(F, FMT, ...) (fprintf)(F, PFLINK(FMT), ##__VA_ARGS__) +#define vfprintf(F, FMT, VA) (vfprintf)(F, PFLINK(FMT), VA) +#define fprintf_unlocked(F, FMT, ...) (fprintf_unlocked)(F, PFLINK(FMT), ##__VA_ARGS__) +#define vfprintf_unlocked(F, FMT, VA) (vfprintf_unlocked)(F, PFLINK(FMT), VA) +#define vscanf(FMT, VA) (vscanf)(SFLINK(FMT), VA) +#define scanf(FMT, ...) (scanf)(SFLINK(FMT), ##__VA_ARGS__) +#define fscanf(F, FMT, ...) (fscanf)(F, SFLINK(FMT), ##__VA_ARGS__) +#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA) +/* clang-format on */ +#endif + +#define stdin SYMBOLIC(stdin) +#define stdout SYMBOLIC(stdout) +#define stderr SYMBOLIC(stderr) + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_H_ */ diff --git a/libc/stdio/unlocked/clearerr_unlocked.S b/libc/stdio/unlocked/clearerr_unlocked.S index b62225c26..8f8c7e9f4 100644 --- a/libc/stdio/unlocked/clearerr_unlocked.S +++ b/libc/stdio/unlocked/clearerr_unlocked.S @@ -22,6 +22,7 @@ // // @param rdi has stream pointer // @see clearerr_unlocked() +// @threadsafe clearerr: mov %rdi,%r11 ezlea clearerr_unlocked,ax diff --git a/libc/stdio/unlocked/feof_unlocked.S b/libc/stdio/unlocked/feof_unlocked.S index a498c5359..d60b6bfad 100644 --- a/libc/stdio/unlocked/feof_unlocked.S +++ b/libc/stdio/unlocked/feof_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has file stream object pointer // @note EOF doesn't count // @see feof_unlocked() +// @threadsafe feof: mov %rdi,%r11 ezlea feof_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/ferror_unlocked.S b/libc/stdio/unlocked/ferror_unlocked.S index 3c5fb28f4..9db9ce695 100644 --- a/libc/stdio/unlocked/ferror_unlocked.S +++ b/libc/stdio/unlocked/ferror_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has file stream object pointer // @note EOF doesn't count // @see ferror_unlocked() +// @threadsafe ferror: mov %rdi,%r11 ezlea ferror_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fflush_unlocked.S b/libc/stdio/unlocked/fflush_unlocked.S index c91963f1c..8463f2876 100644 --- a/libc/stdio/unlocked/fflush_unlocked.S +++ b/libc/stdio/unlocked/fflush_unlocked.S @@ -20,10 +20,14 @@ // Blocks until data from stream buffer is written out. // -// @param rdi is the stream handle +// @param rdi is the stream handle, or 0 for all streams // @return 0 on success or -1 w/ errno // @see fflush_unlocked() -fflush: mov %rdi,%r11 - ezlea fflush_unlocked,ax +// @threadsafe +fflush: ezlea fflush_unlocked,ax + test %rdi,%rdi + jz 1f + mov %rdi,%r11 jmp stdio_unlock +1: jmp *%rax .endfn fflush,globl diff --git a/libc/stdio/unlocked/fgetc_unlocked.S b/libc/stdio/unlocked/fgetc_unlocked.S index 59f250529..ec857991c 100644 --- a/libc/stdio/unlocked/fgetc_unlocked.S +++ b/libc/stdio/unlocked/fgetc_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has stream object pointer // @return byte in range 0..255, or -1 w/ errno // @see fgetc_unlocked() +// @threadsafe fgetc: mov %rdi,%r11 ezlea fgetc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fgets_unlocked.S b/libc/stdio/unlocked/fgets_unlocked.S index 8b155e990..4f2116ec9 100644 --- a/libc/stdio/unlocked/fgets_unlocked.S +++ b/libc/stdio/unlocked/fgets_unlocked.S @@ -30,6 +30,7 @@ // @return rax has rdi on success, NULL on error or // NULL if EOF happens with zero chars read // @see fgets_unlocked() +// @threadsafe fgets: mov %rdx,%r11 ezlea fgets_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fgetwc_unlocked.S b/libc/stdio/unlocked/fgetwc_unlocked.S index 667a67756..ec28e1370 100644 --- a/libc/stdio/unlocked/fgetwc_unlocked.S +++ b/libc/stdio/unlocked/fgetwc_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has stream object pointer // @return wide character or -1 on EOF or error // @see fgetwc_unlocked() +// @threadsafe fgetwc: mov %rdi,%r11 ezlea fgetwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fgetws_unlocked.S b/libc/stdio/unlocked/fgetws_unlocked.S index 1057725c0..f20844603 100644 --- a/libc/stdio/unlocked/fgetws_unlocked.S +++ b/libc/stdio/unlocked/fgetws_unlocked.S @@ -28,6 +28,7 @@ // @param rsi is size of rdi buffer // @param rsi is file stream object pointer // @see fgetws_unlocked() +// @threadsafe fgetws: mov %rdx,%r11 ezlea fgetws_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fileno_unlocked.S b/libc/stdio/unlocked/fileno_unlocked.S index d8804a18e..c67f09214 100644 --- a/libc/stdio/unlocked/fileno_unlocked.S +++ b/libc/stdio/unlocked/fileno_unlocked.S @@ -22,6 +22,7 @@ // // @param rdi has file stream object pointer // @see fileno_unlocked() +// @threadsafe fileno: mov %rdi,%r11 ezlea fileno_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fputc_unlocked.S b/libc/stdio/unlocked/fputc_unlocked.S index a51feb9bc..ca0db43bf 100644 --- a/libc/stdio/unlocked/fputc_unlocked.S +++ b/libc/stdio/unlocked/fputc_unlocked.S @@ -24,6 +24,7 @@ // @param rsi has stream object pointer // @return c as unsigned char if written or -1 w/ errno // @see fputc_unlocked() +// @threadsafe fputc: mov %rsi,%r11 ezlea fputc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fputs_unlocked.S b/libc/stdio/unlocked/fputs_unlocked.S index 1a2cdfa2a..c5e24f7dd 100644 --- a/libc/stdio/unlocked/fputs_unlocked.S +++ b/libc/stdio/unlocked/fputs_unlocked.S @@ -28,6 +28,7 @@ // @param rsi is file object stream pointer // @return strlen(rdi) on success or -1 w/ errno // @see fputs_unlocked() +// @threadsafe fputs: mov %rsi,%r11 ezlea fputs_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fputwc_unlocked.S b/libc/stdio/unlocked/fputwc_unlocked.S index 0c140da48..ee134b8f2 100644 --- a/libc/stdio/unlocked/fputwc_unlocked.S +++ b/libc/stdio/unlocked/fputwc_unlocked.S @@ -24,6 +24,7 @@ // @param rsi has file object stream pointer // @return rax is wide character if written or -1 w/ errno // @see fputwc_unlocked() +// @threadsafe fputwc: mov %rsi,%r11 ezlea fputwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fputws_unlocked.S b/libc/stdio/unlocked/fputws_unlocked.S index 3258d4a7f..ca80d9bde 100644 --- a/libc/stdio/unlocked/fputws_unlocked.S +++ b/libc/stdio/unlocked/fputws_unlocked.S @@ -28,6 +28,7 @@ // @param rsi is file object stream pointer // @return strlen(rdi) on success or -1 w/ errno // @see fputws_unlocked() +// @threadsafe fputws: mov %rsi,%r11 ezlea fputws_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fread_unlocked.S b/libc/stdio/unlocked/fread_unlocked.S index e8084b9b8..4b62cb410 100644 --- a/libc/stdio/unlocked/fread_unlocked.S +++ b/libc/stdio/unlocked/fread_unlocked.S @@ -26,6 +26,7 @@ // @param rcx has file object stream pointer // @return count on success, [0,count) on EOF, 0 on error or count==0 // @see fread_unlocked() +// @threadsafe fread: mov %rcx,%r11 ezlea fread_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/fseeko_unlocked.S b/libc/stdio/unlocked/fseeko_unlocked.S new file mode 100644 index 000000000..dc182fed7 --- /dev/null +++ b/libc/stdio/unlocked/fseeko_unlocked.S @@ -0,0 +1,37 @@ +/*-*- 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" + +// Repositions open file stream. +// +// This function flushes the buffer (unless it's currently in the EOF +// state) and then calls lseek() on the underlying file. If the stream +// is in the EOF state, this function can be used to restore it without +// needing to reopen the file. +// +// @param rdi is stream handle +// @param rsi is offset is the byte delta +// @param rdx is whence and can be SEET_SET, SEEK_CUR, or SEEK_END +// @return 0 on success or -1 w/ errno +// @see fflush_unlocked() +// @threadsafe +fseeko: mov %rdi,%r11 + ezlea fseeko_unlocked,ax + jmp stdio_unlock + .endfn fseeko,globl diff --git a/libc/stdio/unlocked/fwrite_unlocked.S b/libc/stdio/unlocked/fwrite_unlocked.S index 7ea005850..cf57f397a 100644 --- a/libc/stdio/unlocked/fwrite_unlocked.S +++ b/libc/stdio/unlocked/fwrite_unlocked.S @@ -26,6 +26,7 @@ // @param rcx has file object stream pointer // @return count on success, [0,count) on EOF, 0 on error or count==0 // @see fwrite_unlocked() +// @threadsafe fwrite: mov %rcx,%r11 ezlea fwrite_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/getc_unlocked.S b/libc/stdio/unlocked/getc_unlocked.S index 0d06c5218..d92ea8900 100644 --- a/libc/stdio/unlocked/getc_unlocked.S +++ b/libc/stdio/unlocked/getc_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has file stream object pointer // @return byte in range 0..255, or -1 w/ errno // @see fgetc_unlocked() +// @threadsafe getc: mov %rdi,%r11 ezlea fgetwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/getchar_unlocked.S b/libc/stdio/unlocked/getchar_unlocked.S index 532bc1412..fa39d592b 100644 --- a/libc/stdio/unlocked/getchar_unlocked.S +++ b/libc/stdio/unlocked/getchar_unlocked.S @@ -22,6 +22,7 @@ // // @return byte in range 0..255, or -1 w/ errno // @see fgetc_unlocked() +// @threadsafe getchar: mov stdin(%rip),%rdi mov %rdi,%r11 diff --git a/libc/stdio/unlocked/getwc_unlocked.S b/libc/stdio/unlocked/getwc_unlocked.S index c4eca5ee2..70e0047a4 100644 --- a/libc/stdio/unlocked/getwc_unlocked.S +++ b/libc/stdio/unlocked/getwc_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has file stream object pointer // @return wide character or -1 on EOF or error // @see fgetwc_unlocked() +// @threadsafe getwc: mov %rdi,%r11 ezlea fgetwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/getwchar_unlocked.S b/libc/stdio/unlocked/getwchar_unlocked.S index a4d1bf6ea..f6d2d77ca 100644 --- a/libc/stdio/unlocked/getwchar_unlocked.S +++ b/libc/stdio/unlocked/getwchar_unlocked.S @@ -22,6 +22,7 @@ // // @return wide character or -1 on EOF or error // @see fgetwc_unlocked() +// @threadsafe getwchar: mov stdin(%rip),%rdi mov %rdi,%r11 diff --git a/libc/stdio/unlocked/putc_unlocked.S b/libc/stdio/unlocked/putc_unlocked.S index fd5da3886..ae88342b3 100644 --- a/libc/stdio/unlocked/putc_unlocked.S +++ b/libc/stdio/unlocked/putc_unlocked.S @@ -24,6 +24,7 @@ // @param rsi has stream object pointer // @return c as unsigned char if written or -1 w/ errno // @see fputc_unlocked() +// @threadsafe putc: mov %rsi,%r11 ezlea fputc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/putchar_unlocked.S b/libc/stdio/unlocked/putchar_unlocked.S index 77dfa8cba..1b769d986 100644 --- a/libc/stdio/unlocked/putchar_unlocked.S +++ b/libc/stdio/unlocked/putchar_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has character // @return c (as unsigned char) if written or -1 w/ errno // @see fputc_unlocked() +// @threadsafe putchar: mov stdout(%rip),%rsi mov %rsi,%r11 diff --git a/libc/stdio/unlocked/putwc_unlocked.S b/libc/stdio/unlocked/putwc_unlocked.S index d66a91efe..abaa27947 100644 --- a/libc/stdio/unlocked/putwc_unlocked.S +++ b/libc/stdio/unlocked/putwc_unlocked.S @@ -24,6 +24,7 @@ // @param rsi has file object // @return wc if written or -1 w/ errno // @see putwc_unlocked() +// @threadsafe putwc: mov %rsi,%r11 ezlea fputwc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/putwchar_unlocked.S b/libc/stdio/unlocked/putwchar_unlocked.S index 5232ae81b..921ff2ac4 100644 --- a/libc/stdio/unlocked/putwchar_unlocked.S +++ b/libc/stdio/unlocked/putwchar_unlocked.S @@ -23,6 +23,7 @@ // @param rdi has wide character // @return wc if written or -1 w/ errno // @see fputwc_unlocked() +// @threadsafe putwchar: mov stdout(%rip),%rsi mov %rsi,%r11 diff --git a/libc/stdio/unlocked/stdio_unlock.S b/libc/stdio/unlocked/stdio_unlock.S index dd25128a2..3f7a5b94f 100644 --- a/libc/stdio/unlocked/stdio_unlock.S +++ b/libc/stdio/unlocked/stdio_unlock.S @@ -18,53 +18,54 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -#define LOCK 0x2c /* see struct file in stdio.h */ - // Wrapper for applying locking to stdio functions. // // This function is intended to be called by thunks. // -// @param rax has the delegate function pointer +// @param rax is stdio function pointer // @param rdi is passed along as an arg // @param rsi is passed along as an arg // @param rdx is passed along as an arg // @param rcx is passed along as an arg -// @param r8 is passed along as an arg -// @param r9 is passed along as an arg -// @param r10 is passed along as an arg // @param r11 has the FILE* obj pointer // @return rax is passed along as result // @return rdx is passed along as result +// @threadsafe stdio_unlock: push %rbp mov %rsp,%rbp .profilable // acquires mutex - push %rcx + push %rax + push %rdi + push %rsi push %rdx - mov $1,%cl -0: mov LOCK(%r11),%dl # optimistic - test %dl,%dl - je 2f -1: pause # hyperyield - jmp 0b -2: mov %ecx,%edx - xchg LOCK(%r11),%dl # locks bus! - test %dl,%dl - jne 1b - pop %rdx + push %rcx + push %r11 + mov %r11,%rdi + call flockfile + pop %r11 pop %rcx + pop %rdx + pop %rsi + pop %rdi + pop %rax // calls delegate - push %rsi push %r11 + push %rsi # align stack call *%rax - pop %r11 pop %rsi + pop %r11 // releases mutex - movb $0,LOCK(%r11) + push %rax + push %rdx + mov %r11,%rdi + call funlockfile + pop %rdx + pop %rax pop %rbp ret diff --git a/libc/stdio/unlocked/ungetc_unlocked.S b/libc/stdio/unlocked/ungetc_unlocked.S index 967cadd3b..f8b455bdb 100644 --- a/libc/stdio/unlocked/ungetc_unlocked.S +++ b/libc/stdio/unlocked/ungetc_unlocked.S @@ -24,6 +24,7 @@ // @param rds has stream object pointer // @return rax has rdi on success or -1 w/ errno // @see ungetc_unlocked() +// @threadsafe ungetc: mov %rsi,%r11 ezlea ungetc_unlocked,ax jmp stdio_unlock diff --git a/libc/stdio/unlocked/ungetwc_unlocked.S b/libc/stdio/unlocked/ungetwc_unlocked.S index 0c17fce2c..0dad092c4 100644 --- a/libc/stdio/unlocked/ungetwc_unlocked.S +++ b/libc/stdio/unlocked/ungetwc_unlocked.S @@ -24,6 +24,7 @@ // @param rds has stream object pointer // @return rax has rdi on success or -1 w/ errno // @see ungetwc_unlocked() +// @threadsafe ungetwc: mov %rsi,%r11 ezlea ungetwc_unlocked,ax diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c index 1fb65e33c..dc389902b 100644 --- a/libc/stdio/vfprintf.c +++ b/libc/stdio/vfprintf.c @@ -16,45 +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/fmt/fmt.h" -#include "libc/intrin/spinlock.h" -#include "libc/limits.h" #include "libc/stdio/stdio.h" -#include "libc/sysv/errfuns.h" - -struct state { - FILE *f; - int n; -}; - -static int vfprintfputchar(const char *s, struct state *t, size_t n) { - int rc; - if (n) { - _spinlock(&t->f->lock); - if (n == 1 && *s != '\n' && t->f->beg < t->f->size && - t->f->bufmode != _IONBF) { - t->f->buf[t->f->beg++] = *s; - t->n += n; - rc = 0; - } else if (!fwrite_unlocked(s, 1, n, t->f)) { - rc = -1; - } else { - t->n += n; - rc = 0; - } - _spunlock(&t->f->lock); - } else { - rc = 0; - } - return 0; -} +/** + * Formats and writes text to stream. + * @see printf() for further documentation + */ int(vfprintf)(FILE *f, const char *fmt, va_list va) { - struct state st[1] = {{f, 0}}; - if (__fmt(vfprintfputchar, st, fmt, va) != -1) { - return st->n; - } else { - return -1; - } + int rc; + flockfile(f); + rc = (vfprintf_unlocked)(f, fmt, va); + funlockfile(f); + return rc; } diff --git a/libc/stdio/vfprintf_unlocked.c b/libc/stdio/vfprintf_unlocked.c new file mode 100644 index 000000000..940e3e351 --- /dev/null +++ b/libc/stdio/vfprintf_unlocked.c @@ -0,0 +1,61 @@ +/*-*- 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/calls.h" +#include "libc/fmt/fmt.h" +#include "libc/limits.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/errfuns.h" + +struct state { + FILE *f; + int n; +}; + +static int vfprintfputchar(const char *s, struct state *t, size_t n) { + int rc; + if (n) { + if (n == 1 && *s != '\n' && t->f->beg < t->f->size && + t->f->bufmode != _IONBF) { + t->f->buf[t->f->beg++] = *s; + t->n += n; + rc = 0; + } else if (!fwrite_unlocked(s, 1, n, t->f)) { + rc = -1; + } else { + t->n += n; + rc = 0; + } + } else { + rc = 0; + } + return 0; +} + +/** + * Formats and writes text to stream. + * @see printf() for further documentation + */ +int(vfprintf_unlocked)(FILE *f, const char *fmt, va_list va) { + int rc; + struct state st[1] = {{f, 0}}; + if ((rc = __fmt(vfprintfputchar, st, fmt, va)) != -1) { + rc = st->n; + } + return rc; +} diff --git a/libc/stdio/vprintf.c b/libc/stdio/vprintf.c index 059923297..a1e273c98 100644 --- a/libc/stdio/vprintf.c +++ b/libc/stdio/vprintf.c @@ -18,6 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/** + * Formats and writes text to stdout. + * @see printf() for further documentation + */ int(vprintf)(const char* fmt, va_list va) { return (vfprintf)(stdout, fmt, va); } diff --git a/test/libc/stdio/fputc_test.c b/test/libc/stdio/fputc_test.c index 9c2d47888..98253452b 100644 --- a/test/libc/stdio/fputc_test.c +++ b/test/libc/stdio/fputc_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/stdio/stdio.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" FILE *f; @@ -60,3 +61,13 @@ TEST(fgetc, testUnbuffered) { EXPECT_TRUE(feof(f)); EXPECT_NE(-1, fclose(f)); } + +BENCH(fputc, bench) { + FILE *f; + ASSERT_NE(NULL, (f = fopen("/dev/null", "w"))); + EZBENCH2("fputc", donothing, fputc('E', f)); + flockfile(f); + EZBENCH2("fputc_unlocked", donothing, fputc_unlocked('E', f)); + funlockfile(f); + fclose(f); +} diff --git a/third_party/mbedtls/ssl.h b/third_party/mbedtls/ssl.h index 6676e637d..8bc35e19d 100644 --- a/third_party/mbedtls/ssl.h +++ b/third_party/mbedtls/ssl.h @@ -12,20 +12,6 @@ COSMOPOLITAN_C_START_ /* clang-format off */ -#define MBEDTLS_ERR_NET_SOCKET_FAILED -0x0042 /*< Failed to open a socket. */ -#define MBEDTLS_ERR_NET_CONNECT_FAILED -0x0044 /*< The connection to the given server / port failed. */ -#define MBEDTLS_ERR_NET_BIND_FAILED -0x0046 /*< Binding of the socket failed. */ -#define MBEDTLS_ERR_NET_LISTEN_FAILED -0x0048 /*< Could not listen on the socket. */ -#define MBEDTLS_ERR_NET_ACCEPT_FAILED -0x004A /*< Could not accept the incoming connection. */ -#define MBEDTLS_ERR_NET_RECV_FAILED -0x004C /*< Reading information from the socket failed. */ -#define MBEDTLS_ERR_NET_SEND_FAILED -0x004E /*< Sending information through the socket failed. */ -#define MBEDTLS_ERR_NET_CONN_RESET -0x0050 /*< Connection was reset by peer. */ -#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /*< Failed to get an IP address for the given hostname. */ -#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /*< Buffer is too small to hold the data. */ -#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /*< The context is invalid, eg because it was free()ed. */ -#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 /*< Polling the net context failed. */ -#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 /*< Input invalid. */ - /* * SSL Error codes */ diff --git a/tool/build/lib/eztls.c b/tool/build/lib/eztls.c index dc6b6a35a..d863966a4 100644 --- a/tool/build/lib/eztls.c +++ b/tool/build/lib/eztls.c @@ -29,6 +29,7 @@ #include "third_party/mbedtls/ctr_drbg.h" #include "third_party/mbedtls/ecp.h" #include "third_party/mbedtls/error.h" +#include "third_party/mbedtls/net_sockets.h" #include "third_party/mbedtls/platform.h" #include "third_party/mbedtls/ssl.h" #include "tool/build/lib/eztls.h" diff --git a/tool/build/runit.c b/tool/build/runit.c index b11bce9c7..7ab830fed 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -51,6 +51,7 @@ #include "libc/time/time.h" #include "libc/x/x.h" #include "net/https/https.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" @@ -112,6 +113,7 @@ static const struct addrinfo kResolvHints = {.ai_family = AF_INET, int g_sock; char *g_prog; +long g_backoff; char *g_runitd; jmp_buf g_jmpbuf; uint16_t g_sshport; @@ -308,7 +310,7 @@ TryAgain: freeaddrinfo(ai); } -static void Send(const void *output, size_t outputsize) { +static bool Send(const void *output, size_t outputsize) { int rc, have; static bool once; static z_stream zs; @@ -326,11 +328,17 @@ static void Send(const void *output, size_t outputsize) { rc = deflate(&zs, Z_SYNC_FLUSH); CHECK_NE(Z_STREAM_ERROR, rc); have = sizeof(zbuf) - zs.avail_out; - CHECK_EQ(have, mbedtls_ssl_write(&ezssl, zbuf, have)); + rc = mbedtls_ssl_write(&ezssl, zbuf, have); + if (rc == MBEDTLS_ERR_NET_CONN_RESET) { + usleep((g_backoff = (g_backoff + 1000) * 2)); + return false; + } + CHECK_EQ(have, rc); } while (!zs.avail_out); + return true; } -void SendRequest(void) { +int SendRequest(void) { int fd; char *p; size_t i; @@ -357,22 +365,30 @@ void SendRequest(void) { q = mempcpy(q, name, namesize); assert(hdrsize == q - hdr); DEBUGF("running %s on %s", g_prog, g_hostname); - Send(hdr, hdrsize); - Send(p, progsize); - CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0)); + if (Send(hdr, hdrsize) && Send(p, progsize)) { + if (!(rc = EzTlsFlush(&ezbio, 0, 0))) { + rc = 0; + } else if (rc == MBEDTLS_ERR_NET_CONN_RESET) { + rc = -1; + } else { + CHECK_EQ(0, rc); + } + } else { + rc = -1; + } CHECK_NE(-1, munmap(p, st.st_size)); CHECK_NE(-1, close(fd)); + return rc; } bool Recv(unsigned char *p, size_t n) { size_t i, rc; - static long backoff; for (i = 0; i < n; i += rc) { do { rc = mbedtls_ssl_read(&ezssl, p + i, n - i); } while (rc == MBEDTLS_ERR_SSL_WANT_READ); if (!rc || rc == MBEDTLS_ERR_NET_CONN_RESET) { - usleep((backoff = (backoff + 1000) * 2)); + usleep((g_backoff = (g_backoff + 1000) * 2)); return false; } else if (rc < 0) { TlsDie("read response failed", rc); @@ -442,8 +458,7 @@ int RunOnHost(char *spec) { WARNF("warning: got connection reset in handshake"); close(g_sock); } - SendRequest(); - } while ((rc = ReadResponse()) == -1); + } while ((rc = SendRequest()) == -1 || (rc = ReadResponse()) == -1); return rc; } diff --git a/tool/build/runitd.c b/tool/build/runitd.c index f5983ef9b..cf4d92128 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -42,6 +42,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sa.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" @@ -110,7 +111,12 @@ void OnInterrupt(int sig) { void OnChildTerminated(int sig) { int ws, pid; + sigset_t ss, oldss; + sigfillset(&ss); + sigdelset(&ss, SIGTERM); + sigprocmask(SIG_BLOCK, &ss, &oldss); for (;;) { + INFOF("waitpid"); if ((pid = waitpid(-1, &ws, WNOHANG)) != -1) { if (pid) { if (WIFEXITED(ws)) { @@ -127,6 +133,7 @@ void OnChildTerminated(int sig) { FATALF("waitpid failed in sigchld"); } } + sigprocmask(SIG_SETMASK, &oldss, 0); } wontreturn void ShowUsage(FILE *f, int rc) { @@ -229,6 +236,7 @@ void SendExitMessage(int rc) { msg[0 + 3] = (RUNITD_MAGIC & 0x000000ff) >> 000; msg[4] = kRunitExit; msg[5] = rc; + INFOF("mbedtls_ssl_write"); CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg))); CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0)); } @@ -247,6 +255,7 @@ void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf, msg[5 + 1] = (size & 0x00ff0000) >> 020; msg[5 + 2] = (size & 0x0000ff00) >> 010; msg[5 + 3] = (size & 0x000000ff) >> 000; + INFOF("mbedtls_ssl_write"); CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg))); while (size) { CHECK_NE(-1, (rc = mbedtls_ssl_write(&ezssl, buf, size))); @@ -294,6 +303,7 @@ void Recv(void *output, size_t outputsize) { // get another fixed-size data packet from network // pass along error conditions to caller // pass along eof condition to zlib + INFOF("mbedtls_ssl_read"); received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf)); if (received < 0) TlsDie("read failed", received); // decompress packet completely @@ -347,12 +357,14 @@ void HandleClient(void) { /* read request to run program */ addrsize = sizeof(addr); + INFOF("accept"); CHECK_NE(-1, (g_clifd = accept4(g_servfd, &addr, &addrsize, SOCK_CLOEXEC))); if (fork()) { close(g_clifd); return; } EzFd(g_clifd); + INFOF("EzHandshake"); EzHandshake(); addrstr = gc(DescribeAddress(&addr)); DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr); @@ -376,6 +388,7 @@ void HandleClient(void) { } CHECK_NE(-1, (g_exefd = creat(g_exepath, 0700))); LOGIFNEG1(ftruncate(g_exefd, filesize)); + INFOF("xwrite"); CHECK_NE(-1, xwrite(g_exefd, exe, filesize)); LOGIFNEG1(close(g_exefd)); @@ -425,6 +438,7 @@ void HandleClient(void) { fds[0].events = POLLIN; fds[1].fd = pipefds[0]; fds[1].events = POLLIN; + INFOF("poll"); events = poll(fds, ARRAYLEN(fds), (deadline - now) * 1000); CHECK_NE(-1, events); // EINTR shouldn't be possible if (fds[0].revents) { @@ -440,6 +454,7 @@ void HandleClient(void) { LOGIFNEG1(unlink(g_exepath)); _exit(1); } + INFOF("read"); got = read(pipefds[0], g_buf, sizeof(g_buf)); CHECK_NE(-1, got); // EINTR shouldn't be possible if (!got) { @@ -449,6 +464,7 @@ void HandleClient(void) { fwrite(g_buf, got, 1, stderr); SendOutputFragmentMessage(kRunitStderr, g_buf, got); } + INFOF("waitpid"); CHECK_NE(-1, waitpid(child, &wstatus, 0)); // EINTR shouldn't be possible if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus)) { @@ -463,6 +479,7 @@ void HandleClient(void) { } LOGIFNEG1(unlink(g_exepath)); SendExitMessage(exitcode); + INFOF("mbedtls_ssl_close_notify"); mbedtls_ssl_close_notify(&ezssl); LOGIFNEG1(close(g_clifd)); _exit(0); @@ -524,7 +541,7 @@ void Daemonize(void) { int main(int argc, char *argv[]) { int i; SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk()); - /* __log_level = kLogDebug; */ + __log_level = kLogInfo; GetOpts(argc, argv); for (i = 3; i < 16; ++i) close(i); errno = 0; diff --git a/tool/emacs/cosmo-asm-mode.el b/tool/emacs/cosmo-asm-mode.el index 0ff490313..c96524f61 100644 --- a/tool/emacs/cosmo-asm-mode.el +++ b/tool/emacs/cosmo-asm-mode.el @@ -108,6 +108,7 @@ "protip" "nxbitsafe" "vforksafe" + "threadsafe" "preinitsafe" "asyncsignalsafe" "notasyncsignalsafe" diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 5b60d7d1f..bf44a8800 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -146,6 +146,7 @@ #include "third_party/mbedtls/iana.h" #include "third_party/mbedtls/md.h" #include "third_party/mbedtls/md5.h" +#include "third_party/mbedtls/net_sockets.h" #include "third_party/mbedtls/oid.h" #include "third_party/mbedtls/pk.h" #include "third_party/mbedtls/rsa.h" diff --git a/tool/net/wb.c b/tool/net/wb.c index 7ad4f960c..62979fe68 100644 --- a/tool/net/wb.c +++ b/tool/net/wb.c @@ -52,6 +52,7 @@ #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:" From 312ed5c67c1f277b1da2d8156c32a2e8a835a685 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 23 May 2022 10:15:53 -0700 Subject: [PATCH 32/40] Fix some issues and do some code cleanup --- examples/greenbean.c | 108 ++++-- libc/calls/calls.h | 10 +- libc/calls/clock_gettime.c | 2 - libc/calls/ioctl_tcsets-nt.c | 5 +- libc/calls/ischardev.c | 14 +- libc/calls/now.c | 5 +- libc/calls/{virtualmax.c => nowl.S} | 23 +- libc/calls/setitimer-nt.c | 8 +- libc/calls/uname.c | 3 +- .../fflush_unlocked.S => calls/virtualmax.S} | 30 +- libc/intrin/asan.c | 103 +----- libc/intrin/assertfail.greg.c | 1 - libc/intrin/describeflags.internal.h | 1 + libc/intrin/describepersonalityflags.c | 43 +++ libc/intrin/getpid.c | 10 + libc/intrin/gettid.greg.c | 21 +- libc/intrin/intrin.mk | 11 +- libc/intrin/kprintf.greg.c | 2 +- libc/intrin/ntgetversion.c | 4 +- libc/intrin/sched_yield.S | 38 ++- libc/intrin/tls.greg.c | 21 +- libc/nexgen32e/threaded.h | 4 +- libc/nt/enum/version.h | 17 +- libc/nt/version.h | 18 +- libc/runtime/clone-linux.S | 55 +++ libc/runtime/clone.c | 57 +--- libc/runtime/isheap.c | 8 +- libc/runtime/memtrack.internal.h | 5 +- libc/runtime/stackchkfail.c | 58 +--- libc/stdio/fclose.c | 1 + libc/stdio/fflush.c | 78 +---- libc/stdio/fflush_unlocked.c | 98 ++++++ libc/stdio/fflushimpl.c | 2 + libc/stdio/fgetln.c | 30 +- libc/stdio/flockfile.c | 22 +- libc/stdio/fputc.c | 2 +- libc/stdio/ftrylockfile.c | 3 + libc/stdio/funlockfile.c | 16 +- libc/stdio/getdelim.c | 69 +--- libc/stdio/getdelim_unlocked.c | 81 +++++ libc/stdio/getline.c | 12 +- libc/stdio/stdio.h | 11 +- libc/sysv/consts.sh | 12 - libc/sysv/consts/ADDR_COMPAT_LAYOUT.S | 2 - libc/sysv/consts/ADDR_LIMIT_32BIT.S | 2 - libc/sysv/consts/ADDR_LIMIT_3GB.S | 2 - libc/sysv/consts/ADDR_NO_RANDOMIZE.S | 2 - libc/sysv/consts/FDPIC_FUNCPTRS.S | 2 - libc/sysv/consts/MMAP_PAGE_ZERO.S | 2 - libc/sysv/consts/READ_IMPLIES_EXEC.S | 2 - libc/sysv/consts/SHORT_INODE.S | 2 - libc/sysv/consts/STICKY_TIMEOUTS.S | 2 - libc/sysv/consts/UNAME26.S | 2 - libc/sysv/consts/WHOLE_SECONDS.S | 2 - libc/sysv/consts/personality.h | 39 +-- test/libc/calls/open_test.c | 12 + test/libc/intrin/asan_test.c | 21 ++ test/libc/intrin/gettid_test.c | 37 +++ test/libc/intrin/tls_test.c | 2 + test/libc/rand/rand64_test.c | 17 +- test/libc/runtime/ape_test.c | 6 + test/libc/runtime/clone_test.c | 5 +- test/libc/stdio/fgetln_test.c | 87 +++++ third_party/libcxx/errno.h | 314 ------------------ third_party/lua/lrepl.c | 14 +- third_party/lua/lua.main.c | 6 +- third_party/make/error.c | 45 +-- third_party/python/Lib/test/test_doctest.py | 2 +- third_party/python/Lib/test/test_module.py | 2 +- third_party/python/Modules/socketmodule.c | 5 +- tool/net/help.txt | 3 + tool/net/redbean.c | 101 ++---- 72 files changed, 880 insertions(+), 982 deletions(-) rename libc/calls/{virtualmax.c => nowl.S} (82%) rename libc/{stdio/unlocked/fflush_unlocked.S => calls/virtualmax.S} (82%) create mode 100644 libc/intrin/describepersonalityflags.c create mode 100644 libc/runtime/clone-linux.S create mode 100644 libc/stdio/fflush_unlocked.c create mode 100644 libc/stdio/getdelim_unlocked.c delete mode 100644 libc/sysv/consts/ADDR_COMPAT_LAYOUT.S delete mode 100644 libc/sysv/consts/ADDR_LIMIT_32BIT.S delete mode 100644 libc/sysv/consts/ADDR_LIMIT_3GB.S delete mode 100644 libc/sysv/consts/ADDR_NO_RANDOMIZE.S delete mode 100644 libc/sysv/consts/FDPIC_FUNCPTRS.S delete mode 100644 libc/sysv/consts/MMAP_PAGE_ZERO.S delete mode 100644 libc/sysv/consts/READ_IMPLIES_EXEC.S delete mode 100644 libc/sysv/consts/SHORT_INODE.S delete mode 100644 libc/sysv/consts/STICKY_TIMEOUTS.S delete mode 100644 libc/sysv/consts/UNAME26.S delete mode 100644 libc/sysv/consts/WHOLE_SECONDS.S create mode 100644 test/libc/stdio/fgetln_test.c diff --git a/examples/greenbean.c b/examples/greenbean.c index 3698fcf45..275bab40b 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -18,6 +18,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" +#include "libc/limits.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -96,11 +97,12 @@ "Referrer-Policy: origin\r\n" \ "Cache-Control: private; max-age=0\r\n" -int workers; -int messages; -int connections; -const char *status; +_Atomic(int) workers; +_Atomic(int) messages; +_Atomic(int) listening; +_Atomic(int) connections; _Atomic(int) closingtime; +const char *volatile status; int Worker(void *id) { int server, yes = 1; @@ -132,6 +134,7 @@ int Worker(void *id) { listen(server, 1); // connection loop + ++listening; while (!closingtime) { struct tm tm; int64_t unixts; @@ -167,7 +170,7 @@ int Worker(void *id) { continue; } - asm volatile("lock incl\t%0" : "+m"(connections)); + ++connections; // message loop do { @@ -177,12 +180,12 @@ int Worker(void *id) { if ((got = read(client, inbuf, sizeof(inbuf))) <= 0) break; // check that client message wasn't fragmented into more reads if (!(inmsglen = ParseHttpMessage(&msg, inbuf, got))) break; - asm volatile("lock incl\t%0" : "+m"(messages)); + ++messages; #if LOGGING // log the incoming http message clientip = ntohl(clientaddr.sin_addr.s_addr); - kprintf("#%.4x get some %d.%d.%d.%d:%d %#.*s\n", (intptr_t)id, + kprintf("%6P get some %d.%d.%d.%d:%d %#.*s\n", (clientip & 0xff000000) >> 030, (clientip & 0x00ff0000) >> 020, (clientip & 0x0000ff00) >> 010, (clientip & 0x000000ff) >> 000, ntohs(clientaddr.sin_port), msg.uri.b - msg.uri.a, @@ -200,7 +203,7 @@ int Worker(void *id) { p = stpcpy(outbuf, "HTTP/1.1 200 OK\r\n" STANDARD_RESPONSE_HEADERS "Content-Type: text/html; charset=utf-8\r\n" "Date: "); - clock_gettime(CLOCK_REALTIME, &ts), unixts = ts.tv_sec; + clock_gettime(0, &ts), unixts = ts.tv_sec; p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm)); p = stpcpy(p, "\r\nContent-Length: "); p = FormatInt32(p, strlen(q)); @@ -218,7 +221,7 @@ int Worker(void *id) { "HTTP/1.1 404 Not Found\r\n" STANDARD_RESPONSE_HEADERS "Content-Type: text/html; charset=utf-8\r\n" "Date: "); - clock_gettime(CLOCK_REALTIME, &ts), unixts = ts.tv_sec; + clock_gettime(0, &ts), unixts = ts.tv_sec; p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm)); p = stpcpy(p, "\r\nContent-Length: "); p = FormatInt32(p, strlen(q)); @@ -238,13 +241,14 @@ int Worker(void *id) { (msg.method == kHttpGet || msg.method == kHttpHead)); DestroyHttpMessage(&msg); close(client); - asm volatile("lock decl\t%0" : "+m"(connections)); + --connections; } + --listening; // inform the parent that this clone has finished WorkerFinished: close(server); - asm volatile("lock decl\t%0" : "+m"(workers)); + --workers; return 0; } @@ -253,35 +257,85 @@ void OnCtrlC(int sig) { status = " shutting down..."; } +void PrintStatus(void) { + kprintf("\r\e[K\e[32mgreenbean\e[0m " + "workers=%d " + "listening=%d " + "connections=%d " + "messages=%d%s ", + workers, listening, connections, messages, status); +} + int main(int argc, char *argv[]) { + char **tls; + char **stack; int i, threads; uint32_t *hostips; // ShowCrashReports(); - sigaction(SIGINT, &(struct sigaction){.sa_handler = OnCtrlC}, 0); + + // listen for ctrl-c, hangup, and kill which shut down greenbean + status = ""; + struct sigaction sa = {.sa_handler = OnCtrlC}; + sigaction(SIGHUP, &sa, 0); + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + + // print all the ips that 0.0.0.0 will bind for (hostips = GetHostIps(), i = 0; hostips[i]; ++i) { kprintf("listening on http://%d.%d.%d.%d:%d\n", (hostips[i] & 0xff000000) >> 030, (hostips[i] & 0x00ff0000) >> 020, (hostips[i] & 0x0000ff00) >> 010, (hostips[i] & 0x000000ff) >> 000, PORT); } - threads = argc > 1 ? atoi(argv[1]) : 0; - if (!threads) threads = GetCpuCount(); - workers = threads; - for (i = 0; i < threads; ++i) { - char *tls = __initialize_tls(malloc(64)); - void *stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, - MAP_STACK | MAP_ANONYMOUS, -1, 0); - CHECK_NE(-1, clone(Worker, stack, GetStackSize(), - CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_SETTLS, - (void *)(intptr_t)i, 0, tls, 64, 0)); + + // spawn over 9,000 worker threads + tls = 0; + stack = 0; + threads = argc > 1 ? atoi(argv[1]) : GetCpuCount(); + if ((1 <= threads && threads <= INT_MAX) && + (tls = malloc(threads * sizeof(*tls))) && + (stack = malloc(threads * sizeof(*stack)))) { + if (!threads) threads = GetCpuCount(); + for (i = 0; i < threads; ++i) { + if ((tls[i] = __initialize_tls(malloc(64))) && + (stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { + ++workers; + if (clone(Worker, stack[i], GetStackSize(), + CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_SETTID | + CLONE_CHILD_CLEARTID, + (void *)(intptr_t)i, 0, tls[i], 64, + (int *)(tls[i] + 0x38)) == -1) { + --workers; + kprintf("error: clone(%d) failed %m\n", i); + } + } else { + kprintf("error: mmap(%d) failed %m\n", i); + } + if (!(i % 500)) { + PrintStatus(); + } + } + } else { + kprintf("error: invalid number of threads\n"); } - status = ""; + + // wait for workers to terminate while (workers) { - kprintf( - "\r\e[K\e[32mgreenbean\e[0m workers=%d connections=%d messages=%d%s ", - workers, connections, messages, status); + PrintStatus(); usleep(HEARTBEAT * 1000); } + + // clean up terminal line kprintf("\r\e[K"); + + // clean up memory + for (i = 0; i < threads; ++i) { + if (stack) munmap(stack[i], GetStackSize()); + if (tls) free(tls[i]); + } + free(hostips); + free(stack); + free(tls); } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 5d29a9a98..455136d21 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -130,18 +130,18 @@ int geteuid(void) nosideeffect; int getgid(void) nosideeffect; int gethostname(char *, size_t); int getloadavg(double *, int); -int getpgid(int); +int getpgid(int) nosideeffect libcesque; int getpgrp(void) nosideeffect; -int getpid(void); +int getpid(void) nosideeffect libcesque; int getppid(void); int getpriority(int, unsigned); int getresgid(uint32_t *, uint32_t *, uint32_t *); int getresuid(uint32_t *, uint32_t *, uint32_t *); int getrlimit(int, struct rlimit *); int getrusage(int, struct rusage *); -int getsid(int) nosideeffect; -int gettid(void); -int getuid(void) nosideeffect; +int getsid(int) nosideeffect libcesque; +int gettid(void) libcesque; +int getuid(void) nosideeffect libcesque; int kill(int, int); int killpg(int, int); int link(const char *, const char *) dontthrow; diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 0ccd93dc2..f7deab6e6 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -80,8 +80,6 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { * Returns fast system clock_gettime() if it exists. */ void *__get_clock_gettime(void) { - // TODO(jart): Re-enable this. - return 0; void *vdso; static bool once; static void *result; diff --git a/libc/calls/ioctl_tcsets-nt.c b/libc/calls/ioctl_tcsets-nt.c index 9eb023cad..126b57d2f 100644 --- a/libc/calls/ioctl_tcsets-nt.c +++ b/libc/calls/ioctl_tcsets-nt.c @@ -24,6 +24,7 @@ #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/enum/version.h" +#include "libc/nt/version.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" @@ -57,7 +58,7 @@ textwindows int ioctl_tcsets_nt(int ignored, uint64_t request, if (tio->c_lflag & (IEXTEN | ISIG)) { inmode |= kNtEnableProcessedInput; } - if (NtGetVersion() >= kNtVersionWindows10) { + if (IsAtLeastWindows10()) { inmode |= kNtEnableVirtualTerminalInput; } ok = SetConsoleMode(in, inmode); @@ -71,7 +72,7 @@ textwindows int ioctl_tcsets_nt(int ignored, uint64_t request, if (!(tio->c_oflag & ONLCR)) { outmode |= kNtDisableNewlineAutoReturn; } - if (NtGetVersion() >= kNtVersionWindows10) { + if (IsAtLeastWindows10()) { outmode |= kNtEnableVirtualTerminalProcessing; } ok = SetConsoleMode(out, outmode); diff --git a/libc/calls/ischardev.c b/libc/calls/ischardev.c index 82fbe7d51..a310e958f 100644 --- a/libc/calls/ischardev.c +++ b/libc/calls/ischardev.c @@ -16,17 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/metastat.internal.h" -#include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.internal.h" /** * Returns true if file descriptor is backed by character i/o. @@ -44,15 +40,7 @@ bool32 ischardev(int fd) { int e; union metastat st; if (__isfdkind(fd, kFdZip)) { - e = errno; - if (weaken(__zipos_fstat)( - (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, &st.cosmo) != - -1) { - return S_ISCHR(st.cosmo.st_mode); - } else { - errno = e; - return false; - } + return false; } else if (IsMetal()) { return true; } else if (!IsWindows()) { diff --git a/libc/calls/now.c b/libc/calls/now.c index a7ec88b88..e32879fd5 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -97,7 +97,8 @@ static long double nowl_vdso(void) { long double nowl_setup(void) { uint64_t ticks; - if ((g_now.clock_gettime = __get_clock_gettime())) { + if (0 && (g_now.clock_gettime = __get_clock_gettime())) { + // TODO(jart): Re-enable this. nowl = nowl_vdso; } else if (X86_HAVE(INVTSC)) { RefreshTime(); @@ -107,5 +108,3 @@ long double nowl_setup(void) { } return nowl(); } - -long double (*nowl)(void) = nowl_setup; diff --git a/libc/calls/virtualmax.c b/libc/calls/nowl.S similarity index 82% rename from libc/calls/virtualmax.c rename to libc/calls/nowl.S index 92df148c7..d83b1f59d 100644 --- a/libc/calls/virtualmax.c +++ b/libc/calls/nowl.S @@ -1,5 +1,5 @@ -/*-*- 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│ +/*-*- 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 │ │ │ @@ -16,13 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/macros.internal.h" -/** - * Maximum amount of virtual memory in bytes. - * - * mmap() will return ENOMEM once this is reached. - * - * By default no limit is imposed. - */ -size_t __virtualmax = -1; + .initbss 201,_init_nowl +nowl: .quad 0 + .endobj nowl,globl + .previous + + .init.start 201,_init_nowl + ezlea nowl_setup,ax + stosq + .init.end 201,_init_nowl diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c index 52272b2e2..3dd838e30 100644 --- a/libc/calls/setitimer-nt.c +++ b/libc/calls/setitimer-nt.c @@ -81,7 +81,13 @@ textwindows void _check_sigalrm(void) { textwindows int sys_setitimer_nt(int which, const struct itimerval *newvalue, struct itimerval *out_opt_oldvalue) { long double elapsed, untilnext; - if (which != ITIMER_REAL) return einval(); + if (which != ITIMER_REAL || + (newvalue && (!(0 <= newvalue->it_value.tv_usec && + newvalue->it_value.tv_usec < 1000000) || + !(0 <= newvalue->it_interval.tv_usec && + newvalue->it_interval.tv_usec < 1000000)))) { + return einval(); + } if (out_opt_oldvalue) { if (__hastimer) { elapsed = nowl() - __lastalrm; diff --git a/libc/calls/uname.c b/libc/calls/uname.c index fa187db12..a7b22023e 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -48,7 +48,7 @@ static inline textwindows noasan int NtGetBuildNumber(void) { * @return 0 on success, or -1 w/ errno */ int uname(struct utsname *lool) { - int rc, v; + int rc; char *out, *p; size_t i, j, len; char tmp[sizeof(struct utsname)]; @@ -87,7 +87,6 @@ int uname(struct utsname *lool) { rc = enosys(); } } else { - v = NtGetVersion(); p = lool->release; p = FormatUint32(p, NtGetMajorVersion()), *p++ = '.'; p = FormatUint32(p, NtGetMinorVersion()), *p++ = '-'; diff --git a/libc/stdio/unlocked/fflush_unlocked.S b/libc/calls/virtualmax.S similarity index 82% rename from libc/stdio/unlocked/fflush_unlocked.S rename to libc/calls/virtualmax.S index 8463f2876..655ecba25 100644 --- a/libc/stdio/unlocked/fflush_unlocked.S +++ b/libc/calls/virtualmax.S @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -18,16 +18,20 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -// Blocks until data from stream buffer is written out. + .initbss 201,_init___virtualmax + +// Maximum amount of virtual memory in bytes. // -// @param rdi is the stream handle, or 0 for all streams -// @return 0 on success or -1 w/ errno -// @see fflush_unlocked() -// @threadsafe -fflush: ezlea fflush_unlocked,ax - test %rdi,%rdi - jz 1f - mov %rdi,%r11 - jmp stdio_unlock -1: jmp *%rax - .endfn fflush,globl +// mmap() will return ENOMEM once this is reached. +// +// By default no limit is imposed. +__virtualmax: + .quad 0 + .endobj __virtualmax,globl + .previous + + .init.start 201,_init___virtualmax + push $-1 + pop %rax + stosq + .init.end 201,_init___virtualmax diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index d8bf1bd89..93f63602a 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -199,7 +199,7 @@ static char *__asan_utf8cpy(char *p, unsigned c) { return p; } -static void *__asan_memset(void *p, char c, size_t n) { +static void __asan_memset(void *p, char c, size_t n) { char *b; size_t i; uint64_t x; @@ -207,29 +207,29 @@ static void *__asan_memset(void *p, char c, size_t n) { x = 0x0101010101010101ul * (c & 255); switch (n) { case 0: - return p; + break; case 1: __builtin_memcpy(b, &x, 1); - return p; + break; case 2: __builtin_memcpy(b, &x, 2); - return p; + break; case 3: __builtin_memcpy(b, &x, 2); __builtin_memcpy(b + 1, &x, 2); - return p; + break; case 4: __builtin_memcpy(b, &x, 4); - return p; + break; case 5: case 6: case 7: __builtin_memcpy(b, &x, 4); __builtin_memcpy(b + n - 4, &x, 4); - return p; + break; case 8: __builtin_memcpy(b, &x, 8); - return p; + break; case 9: case 10: case 11: @@ -240,7 +240,7 @@ static void *__asan_memset(void *p, char c, size_t n) { case 16: __builtin_memcpy(b, &x, 8); __builtin_memcpy(b + n - 8, &x, 8); - return p; + break; default: if (n <= 64) { i = 0; @@ -253,83 +253,10 @@ static void *__asan_memset(void *p, char c, size_t n) { } else { __repstosb(p, c, n); } - return p; + break; } } -static void *__asan_mempcpy(void *dst, const void *src, size_t n) { - size_t i; - char *d; - const char *s; - uint64_t a, b; - d = dst; - s = src; - switch (n) { - case 0: - return d; - case 1: - *d = *s; - return d + 1; - case 2: - __builtin_memcpy(&a, s, 2); - __builtin_memcpy(d, &a, 2); - return d + 2; - case 3: - __builtin_memcpy(&a, s, 2); - __builtin_memcpy(&b, s + 1, 2); - __builtin_memcpy(d, &a, 2); - __builtin_memcpy(d + 1, &b, 2); - return d + 3; - case 4: - __builtin_memcpy(&a, s, 4); - __builtin_memcpy(d, &a, 4); - return d + 4; - case 5: - case 6: - case 7: - __builtin_memcpy(&a, s, 4); - __builtin_memcpy(&b, s + n - 4, 4); - __builtin_memcpy(d, &a, 4); - __builtin_memcpy(d + n - 4, &b, 4); - return d + n; - case 8: - __builtin_memcpy(&a, s, 8); - __builtin_memcpy(d, &a, 8); - return d + 8; - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - __builtin_memcpy(&a, s, 8); - __builtin_memcpy(&b, s + n - 8, 8); - __builtin_memcpy(d, &a, 8); - __builtin_memcpy(d + n - 8, &b, 8); - return d + n; - default: - if (n <= 64) { - i = 0; - do { - __builtin_memcpy(&a, s + i, 8); - asm volatile("" ::: "memory"); - __builtin_memcpy(d + i, &a, 8); - } while ((i += 8) + 8 <= n); - for (; i < n; ++i) d[i] = s[i]; - return d + i; - } else { - return __repmovsb(d, s, n); - } - } -} - -static void *__asan_memcpy(void *dst, const void *src, size_t n) { - __asan_mempcpy(dst, src, n); - return dst; -} - static char *__asan_hexcpy(char *p, uint64_t x, uint8_t k) { while (k) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; return p; @@ -736,7 +663,7 @@ static void __asan_report_memory_origin(const unsigned char *addr, int size, if (_base <= addr && addr < _end) { __asan_report_memory_origin_image((intptr_t)addr, size); } else if (IsAutoFrame((intptr_t)addr >> 16)) { - /* __asan_report_memory_origin_heap(addr, size); */ + __asan_report_memory_origin_heap(addr, size); } } @@ -946,8 +873,8 @@ static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { #define __asan_trace __asan_rawtrace -static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, - struct AsanTrace *bt) { +void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, + struct AsanTrace *bt) { char *p; size_t c; struct AsanExtra *e; @@ -960,7 +887,7 @@ static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, __asan_poison(p + n, c - n, overrun); __asan_memset(p, 0xF9, n); __asan_write48(&e->size, n); - __asan_memcpy(&e->bt, bt, sizeof(*bt)); + __builtin_memcpy(&e->bt, bt, sizeof(*bt)); } return p; } @@ -1082,7 +1009,7 @@ static void *__asan_realloc_grow(void *p, size_t n, size_t m, struct AsanTrace *bt) { char *q; if ((q = __asan_allocate(16, n, kAsanHeapUnderrun, kAsanHeapOverrun, bt))) { - __asan_memcpy(q, p, m); + __builtin_memcpy(q, p, m); __asan_deallocate(p, kAsanHeapRelocated); } return q; diff --git a/libc/intrin/assertfail.greg.c b/libc/intrin/assertfail.greg.c index ceaa381db..7ee853936 100644 --- a/libc/intrin/assertfail.greg.c +++ b/libc/intrin/assertfail.greg.c @@ -46,7 +46,6 @@ relegated wontreturn void __assert_fail(const char *expr, const char *file, } else { rc = 24; } - if (weaken(__die)) weaken(__die)(); __restorewintty(); _Exit(rc); } diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 189dc829f..e797abc9a 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -23,6 +23,7 @@ const char *DescribeMapFlags(int); const char *DescribeProtFlags(int); const char *DescribeRemapFlags(int); const char *DescribeRlimitName(int); +const char *DescribePersonalityFlags(int); const char *DescribeSeccompOperationFlags(int); const char *DescribePollFlags(char *, size_t, int); const char *DescribeStat(int, const struct stat *); diff --git a/libc/intrin/describepersonalityflags.c b/libc/intrin/describepersonalityflags.c new file mode 100644 index 000000000..5e09fde70 --- /dev/null +++ b/libc/intrin/describepersonalityflags.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/filesharemode.h" +#include "libc/sysv/consts/personality.h" + +static const struct DescribeFlags kPersonalityFlags[] = { + {ADDR_COMPAT_LAYOUT, "ADDR_COMPAT_LAYOUT"}, // + {READ_IMPLIES_EXEC, "READ_IMPLIES_EXEC"}, // + {ADDR_LIMIT_3GB, "ADDR_LIMIT_3GB"}, // + {FDPIC_FUNCPTRS, "FDPIC_FUNCPTRS"}, // + {STICKY_TIMEOUTS, "STICKY_TIMEOUTS"}, // + {MMAP_PAGE_ZERO, "MMAP_PAGE_ZERO"}, // + {ADDR_LIMIT_32BIT, "ADDR_LIMIT_32BIT"}, // + {WHOLE_SECONDS, "WHOLE_SECONDS"}, // + {ADDR_NO_RANDOMIZE, "ADDR_NO_RANDOMIZE"}, // + {SHORT_INODE, "SHORT_INODE"}, // + {UNAME26, "UNAME26"}, // +}; + +const char *DescribePersonalityFlags(int x) { + _Alignas(char) static char personalityflags[128]; + return DescribeFlags(personalityflags, sizeof(personalityflags), + kPersonalityFlags, ARRAYLEN(kPersonalityFlags), "", x); +} diff --git a/libc/intrin/getpid.c b/libc/intrin/getpid.c index 0d8113aed..d45b03254 100644 --- a/libc/intrin/getpid.c +++ b/libc/intrin/getpid.c @@ -22,7 +22,17 @@ /** * Returns process id. + * + * This function does not need to issue a system call. The PID is + * tracked by a global variable which is updated atfork(). The only + * exception is when the process is vfork()'d in which case a system + * call shall be issued. + * + * On Linux, and only Linux, the process id is guaranteed to be the same + * as gettid() for the main thread. + * * @asyncsignalsafe + * @threadsafe * @vforksafe */ int getpid(void) { diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.greg.c index 4cc21ca3f..9e3cc2802 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.greg.c @@ -32,8 +32,25 @@ __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; * if this is the main thread. On NetBSD, gettid() for the main thread * is always 1. * + * This function issues a system call. That stops being the case as soon + * as __install_tls() is called. That'll happen automatically, when you + * call clone() and provide the TLS parameter. We assume that when a TLS + * block exists, then + * + * *(int *)(__get_tls() + 0x38) + * + * will contain the thread id. Therefore when issuing clone() calls, the + * `CLONE_CHILD_SETTID` and `CLONE_CHILD_CLEARTID` flags should use that + * index as its `ctid` memory. + * + * gettid (single threaded) l: 126𝑐 41𝑛𝑠 + * gettid (tls enabled) l: 2𝑐 1𝑛𝑠 + * + * The TLS convention is important for reentrant lock performance. + * * @return thread id greater than zero or -1 w/ errno * @asyncsignalsafe + * @threadsafe */ privileged int gettid(void) { int rc; @@ -42,9 +59,7 @@ privileged int gettid(void) { if (__tls_enabled) { rc = *(int *)(__get_tls() + 0x38); - if (rc && rc != -1) { - return rc; - } + return rc; } if (IsWindows()) { diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index e7b05d974..6978c7ce2 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -46,9 +46,14 @@ $(LIBC_INTRIN_A_OBJS): \ OVERRIDE_CFLAGS += \ -foptimize-sibling-calls +# we can't use asan and ubsan because: +# this is asan and ubsan +# we need -ffreestanding because: +# we don't want __builtin_memcpy() calling memcpy() o/$(MODE)/libc/intrin/asan.o \ o/$(MODE)/libc/intrin/ubsan.o: \ OVERRIDE_CFLAGS += \ + -ffreestanding \ -fno-sanitize=all \ -fno-stack-protector @@ -145,12 +150,6 @@ o/$(MODE)/libc/intrin/describeopenflags.greg.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED -o/$(MODE)/libc/intrin/asan.o \ -o/$(MODE)/libc/intrin/ubsan.o: \ - OVERRIDE_CFLAGS += \ - -fno-sanitize=all \ - -fno-stack-protector - o//libc/intrin/memmove.o: \ OVERRIDE_CFLAGS += \ -fno-toplevel-reorder diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index f3340e947..bf7b7021e 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -857,7 +857,6 @@ privileged void kvprintf(const char *fmt, va_list v) { * * Specifiers: * - * - `P` pid * - `c` char * - `o` octal * - `b` binary @@ -873,6 +872,7 @@ privileged void kvprintf(const char *fmt, va_list v) { * - `X` uppercase * - `T` timestamp * - `x` hexadecimal + * - `P` pid (or tid if threaded) * * Types: * diff --git a/libc/intrin/ntgetversion.c b/libc/intrin/ntgetversion.c index 57c7ea551..d458b68b2 100644 --- a/libc/intrin/ntgetversion.c +++ b/libc/intrin/ntgetversion.c @@ -22,9 +22,9 @@ /** * Returns New Technology version, e.g. * - * if (IsWindows() && NtGetVersion() >=k NtVersionWindows10) {...} - * * This can only be called on Windows. + * + * @see IsAtLeastWindows10() */ textwindows noasan int NtGetVersion(void) { return (NtGetPeb()->OSMajorVersion & 0xff) << 8 | NtGetPeb()->OSMinorVersion; diff --git a/libc/intrin/sched_yield.S b/libc/intrin/sched_yield.S index 1060780c4..c0a7d80bd 100644 --- a/libc/intrin/sched_yield.S +++ b/libc/intrin/sched_yield.S @@ -25,16 +25,8 @@ // // @return 0 on success, or -1 w/ errno sched_yield: - push %rbp - mov %rsp,%rbp - testb IsWindows() - jnz 1f - -// UNIX Support - mov __NR_sched_yield,%eax - syscall - jmp 2f +#if SupportsWindows() // Windows Support // // A value of zero, together with the bAlertable parameter set to @@ -44,12 +36,34 @@ sched_yield: // threads ready to run and no user APCs are queued, the function // returns immediately, and the thread continues execution. // ──Quoth MSDN -1: xor %ecx,%ecx + testb IsWindows() + jz 1f + push %rbp + mov %rsp,%rbp + xor %ecx,%ecx xor %edx,%edx ntcall __imp_SleepEx xor %eax,%eax - -2: pop %rbp + pop %rbp ret +#endif + +#if SupportsSystemv() +// UNIX Support +1: mov __NR_sched_yield,%eax +#if SupportsBsd() && SupportsLinux() + clc +#endif + syscall +#if SupportsBsd() + jc systemfive_errno +#endif +#if SupportsLinux() + cmp $-4095,%rax + jae systemfive_error +#endif +#endif + +2: ret .endfn sched_yield,globl .previous diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index 52366662c..e3250d190 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -44,23 +44,28 @@ * 0x003c 0x04 errno * */ -privileged void *__initialize_tls(char tib[hasatleast 64]) { - *(intptr_t *)tib = (intptr_t)tib; - *(intptr_t *)(tib + 0x08) = 0; - *(int *)(tib + 0x10) = -1; // exit code - *(intptr_t *)(tib + 0x30) = (intptr_t)tib; - *(int *)(tib + 0x38) = -1; // tid - *(int *)(tib + 0x3c) = __errno; +privileged void *__initialize_tls(char tib[64]) { + if (tib) { + *(intptr_t *)tib = (intptr_t)tib; + *(intptr_t *)(tib + 0x08) = 0; + *(int *)(tib + 0x10) = -1; // exit code + *(intptr_t *)(tib + 0x30) = (intptr_t)tib; + *(int *)(tib + 0x38) = -1; // tid + *(int *)(tib + 0x3c) = 0; + } return tib; } /** * Installs thread information block on main process. */ -privileged void __install_tls(char tib[hasatleast 64]) { +privileged void __install_tls(char tib[64]) { int ax, dx; uint64_t magic; unsigned char *p; + assert(tib); + assert(!__tls_enabled); + assert(*(int *)(tib + 0x38) != -1); if (IsWindows()) { if (!__tls_index) { __tls_index = TlsAlloc(); diff --git a/libc/nexgen32e/threaded.h b/libc/nexgen32e/threaded.h index 778da129d..868aab33e 100644 --- a/libc/nexgen32e/threaded.h +++ b/libc/nexgen32e/threaded.h @@ -8,8 +8,8 @@ extern bool __threaded; extern bool __tls_enabled; extern unsigned __tls_index; -void *__initialize_tls(char[hasatleast 64]); -void __install_tls(char[hasatleast 64]); +void *__initialize_tls(char[64]); +void __install_tls(char[64]); #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) /** diff --git a/libc/nt/enum/version.h b/libc/nt/enum/version.h index c6f67f1fc..48a45834f 100644 --- a/libc/nt/enum/version.h +++ b/libc/nt/enum/version.h @@ -4,17 +4,18 @@ /** * Known versions of the New Technology executive. + * @see IsAtLeastWindows10() * @see NtGetVersion() */ -#define kNtVersionWindows10 0x0a00 -#define kNtVersionWindows81 0x0603 -#define kNtVersionWindows8 0x0602 -#define kNtVersionWindows7 0x0601 +#define kNtVersionWindows10 0x0a00 +#define kNtVersionWindows81 0x0603 +#define kNtVersionWindows8 0x0602 +#define kNtVersionWindows7 0x0601 #define kNtVersionWindowsVista 0x0600 /* intended baseline */ -#define kNtVersionWindowsXp64 0x0502 /* end of the road */ -#define kNtVersionWindowsXp 0x0501 /* snowball's chance */ -#define kNtVersionWindows2000 0x0500 /* the golden age */ -#define kNtVersionFuture 0x0b00 +#define kNtVersionWindowsXp64 0x0502 /* end of the road */ +#define kNtVersionWindowsXp 0x0501 /* snowball's chance */ +#define kNtVersionWindows2000 0x0500 /* the golden age */ +#define kNtVersionFuture 0x0b00 #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_ENUM_VERSION_H_ */ diff --git a/libc/nt/version.h b/libc/nt/version.h index f7ade11de..c7da8aab0 100644 --- a/libc/nt/version.h +++ b/libc/nt/version.h @@ -8,16 +8,14 @@ bool IsAtLeastWindows10(void) pureconst; bool32 GetVersionEx(struct NtOsVersionInfo *lpVersionInformation); #if defined(__GCC_ASM_FLAG_OUTPUTS__) && !defined(__STRICT_ANSI__) -#define IsAtLeastWindows10() \ - ({ \ - long ReG; \ - bool NoTbelow; \ - asm("mov\t%%gs:96,%1\r\n" \ - "cmpb\t%2,280(%1)" \ - : "=@ccnb"(NoTbelow), "=l"(ReG) \ - : "i"(10)); \ - NoTbelow; \ - }) +#define IsAtLeastWindows10() (GetNtMajorVersion() >= 10) +static pureconst inline unsigned char GetNtMajorVersion(void) { + uintptr_t _x; + asm("mov\t%%gs:96,%q0\r\n" + "mov\t280(%q0),%b0" + : "=q"(_x)); + return _x; +} #endif COSMOPOLITAN_C_END_ diff --git a/libc/runtime/clone-linux.S b/libc/runtime/clone-linux.S new file mode 100644 index 000000000..131e60592 --- /dev/null +++ b/libc/runtime/clone-linux.S @@ -0,0 +1,55 @@ +/*-*- 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" + +// Invokes clone() system call on GNU/Systemd. +// +// @param rdi is flags +// @param rsi is top of stack +// @param rdx is ptid +// @param rcx is ctid +// @param r8 is tls +// @param r9 is func +// @param 8(rsp) is arg +// @return tid of child on success, or -1 w/ errno +sys_clone_linux: + push %rbp + mov %rsp,%rbp + .profilable + push %rbx + mov %rcx,%r10 + mov 16(%rbp),%rbx + mov $56,%eax # __NR_clone + syscall + test %rax,%rax + jz 2f + cmp $-4095,%rax + jae 1f +0: pop %rbx + pop %rbp + ret +1: call systemfive_error + jmp 0b +2: xor %ebp,%ebp # child thread + mov %rbx,%rdi # arg + call *%r9 # func(arg) + xchg %eax,%edi # func(arg) → exitcode + mov $60,%eax # __NR_exit(exitcode) + syscall + .endfn sys_clone_linux,globl,hidden diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 779a12bf7..e15daea30 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -419,40 +419,8 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags, //////////////////////////////////////////////////////////////////////////////// // GNU/SYSTEMD -int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags, - void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { -#ifdef __chibicc__ - return -1; // TODO -#else - int ax; - intptr_t *stack = (intptr_t *)(stk + stksz); - *--stack = (intptr_t)arg; - // %rax = syscall(%rax = __NR_clone, - // %rdi = flags, - // %rsi = child_stack, - // %rdx = parent_tidptr, - // %r10 = child_tidptr, - // %r8 = new_tls); - asm volatile("mov\t%4,%%r10\n\t" // ctid - "mov\t%5,%%r8\n\t" // tls - "mov\t%6,%%r9\n\t" // func - "syscall\n\t" - "test\t%0,%0\n\t" - "jnz\t1f\n\t" - "xor\t%%ebp,%%ebp\n\t" - "pop\t%%rdi\n\t" // arg - "call\t*%%r9\n\t" // func - "xchg\t%%eax,%%edi\n\t" - "mov\t$0x3c,%%eax\n\t" - "syscall\n1:" - : "=a"(ax) - : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "g"(ctid), - "g"(tls), "g"(func), "d"(ptid) - : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (ax > -4096u) errno = -ax, ax = -1; - return ax; -#endif -} +int sys_clone_linux(int flags, char *stk, int *ptid, int *ctid, void *tls, + int (*func)(void *), void *arg); //////////////////////////////////////////////////////////////////////////////// // COSMOPOLITAN @@ -542,13 +510,23 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, int rc; struct CloneArgs *wt; - if (flags & CLONE_THREAD) { - __threaded = true; - } - + // transition program to threaded state if ((flags & CLONE_SETTLS) && !__tls_enabled) { + if (~flags & CLONE_THREAD) { + STRACE("clone() tls w/o thread"); + return einval(); + } + if (__threaded) { + STRACE("clone() tls/non-tls mixed order"); + return einval(); + } __initialize_tls(tibdefault); + *(int *)((char *)tibdefault + 0x38) = gettid(); + *(int *)((char *)tibdefault + 0x3c) = __errno; __install_tls(tibdefault); + __threaded = true; + } else if (flags & CLONE_THREAD) { + __threaded = true; } if (IsAsan() && @@ -566,7 +544,8 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg, ((flags & CLONE_SETTLS) && (tlssz < 64 || (tlssz & 7))))) { rc = einval(); } else if (IsLinux()) { - rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + rc = + sys_clone_linux(flags, (char *)stk + stksz, ptid, ctid, tls, func, arg); } else if (!IsTiny() && (flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) != diff --git a/libc/runtime/isheap.c b/libc/runtime/isheap.c index a532b4380..6d3796e43 100644 --- a/libc/runtime/isheap.c +++ b/libc/runtime/isheap.c @@ -26,7 +26,9 @@ * @assume stack addresses are always greater than heap addresses * @assume stack memory isn't stored beneath %rsp (-mno-red-zone) */ -noasan bool _isheap(void *p) { - return kAutomapStart <= (intptr_t)p && - (intptr_t)p < kAutomapStart + kAutomapSize; +optimizesize noasan bool _isheap(void *p) { + intptr_t x, y; + x = kAutomapStart; + y = x + kAutomapSize; + return x <= (intptr_t)p && (intptr_t)p < y; } diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 43e9efacd..ea64eaec6 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -4,8 +4,7 @@ #include "libc/bits/midpoint.h" #include "libc/dce.h" #include "libc/macros.internal.h" -#include "libc/nt/enum/version.h" -#include "libc/runtime/runtime.h" +#include "libc/nt/version.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/ss.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -29,7 +28,7 @@ COSMOPOLITAN_C_START_ ROUNDUP(VSPACE / FRAMESIZE * (intptr_t)sizeof(struct MemoryInterval), \ FRAMESIZE) #define _kMem(NORMAL, WIN7) \ - (!(IsWindows() && NtGetVersion() < kNtVersionWindows10) ? NORMAL : WIN7) + (!IsWindows() || IsAtLeastWindows10() ? NORMAL : WIN7) struct MemoryInterval { int x; diff --git a/libc/runtime/stackchkfail.c b/libc/runtime/stackchkfail.c index ad42a392d..29caaeb51 100644 --- a/libc/runtime/stackchkfail.c +++ b/libc/runtime/stackchkfail.c @@ -16,59 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/pushpop.h" -#include "libc/nt/enum/version.h" -#include "libc/nt/runtime.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/internal.h" -#include "libc/sysv/consts/fileno.h" -#include "libc/sysv/consts/nr.h" -#define STACK_SMASH_MESSAGE "stack smashed\n" - -/** - * Aborts program under enemy fire to avoid being taken alive. - */ -privileged noasan void __stack_chk_fail(void) { - size_t len; - const char *msg; - int64_t ax, cx, si; - if (!IsWindows()) { - msg = STACK_SMASH_MESSAGE; - len = pushpop(sizeof(STACK_SMASH_MESSAGE) - 1); - if (!IsMetal()) { - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_write), "D"(pushpop(STDERR_FILENO)), "S"(msg), - "d"(len) - : "rcx", "r11", "cc", "memory"); - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_exit_group), "D"(pushpop(23)) - : "rcx", "r11", "cc", "memory"); - } - asm volatile("rep outsb" - : "=S"(si), "=c"(cx) - : "0"(msg), "1"(len), "d"(0x3F8 /* COM1 */) - : "memory"); - asm("push\t$0\n\t" - "push\t$0\n\t" - "cli\n\t" - "lidt\t(%rsp)"); - for (;;) asm("ud2"); - } - if (NtGetVersion() < kNtVersionFuture) { - do { - asm volatile("syscall" - : "=a"(ax), "=c"(cx) - : "0"(NtGetVersion() < kNtVersionWindows8 ? 0x0029 - : NtGetVersion() < kNtVersionWindows81 ? 0x002a - : NtGetVersion() < kNtVersionWindows10 ? 0x002b - : 0x002c), - "1"(pushpop(-1L)), "d"(42) - : "r11", "cc", "memory"); - } while (!ax); - } - for (;;) { - TerminateProcess(GetCurrentProcess(), 42); - } +privileged noasan noinstrument void __stack_chk_fail(void) { + kprintf("stack smashed\n"); + __restorewintty(); + _Exit(207); } diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c index cd75a500e..ebb7b6e65 100644 --- a/libc/stdio/fclose.c +++ b/libc/stdio/fclose.c @@ -39,6 +39,7 @@ int fclose(FILE *f) { if (!f) return 0; __fflush_unregister(f); fflush(f); + free_s(&f->getln); if (!f->nofree) { free_s(&f->buf); } diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c index b9411a5bc..5618a8ae4 100644 --- a/libc/stdio/fflush.c +++ b/libc/stdio/fflush.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=8 sts=2 sw=2 fenc=utf-8 :vi│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,83 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/arraylist.internal.h" -#include "libc/bits/bits.h" -#include "libc/bits/pushpop.h" -#include "libc/calls/calls.h" -#include "libc/errno.h" -#include "libc/intrin/spinlock.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/fflush.internal.h" -#include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" -#include "libc/sysv/consts/o.h" /** * Blocks until data from stream buffer is written out. * * @param f is the stream handle, or 0 for all streams * @return is 0 on success or -1 on error + * @threadsafe */ -int fflush_unlocked(FILE *f) { - int rc = 0; - size_t i; - if (!f) { - _spinlock(&__fflush.lock); - for (i = __fflush.handles.i; i; --i) { - if ((f = __fflush.handles.p[i - 1])) { - if (fflush(f) == -1) { - rc = -1; - } - } - } - _spunlock(&__fflush.lock); - } else if (f->fd != -1) { - if (__fflush_impl(f) == -1) { - rc = -1; - } - } else if (f->beg && f->beg < f->size) { - f->buf[f->beg] = 0; - } - return rc; -} - -textstartup int __fflush_register(FILE *f) { +int fflush(FILE *f) { int rc; - size_t i; - struct StdioFlush *sf; - _spinlock(&__fflush.lock); - sf = &__fflush; - if (!sf->handles.p) { - sf->handles.p = sf->handles_initmem; - pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem)); - __cxa_atexit(fflush_unlocked, 0, 0); - } - for (i = sf->handles.i; i; --i) { - if (!sf->handles.p[i - 1]) { - sf->handles.p[i - 1] = f; - _spunlock(&__fflush.lock); - return 0; - } - } - rc = append(&sf->handles, &f); - _spunlock(&__fflush.lock); + if (f) flockfile(f); + rc = fflush_unlocked(f); + if (f) funlockfile(f); return rc; } - -void __fflush_unregister(FILE *f) { - size_t i; - struct StdioFlush *sf; - _spinlock(&__fflush.lock); - sf = &__fflush; - sf = pushpop(sf); - for (i = sf->handles.i; i; --i) { - if (sf->handles.p[i - 1] == f) { - pushmov(&sf->handles.p[i - 1], 0); - break; - } - } - _spunlock(&__fflush.lock); -} diff --git a/libc/stdio/fflush_unlocked.c b/libc/stdio/fflush_unlocked.c new file mode 100644 index 000000000..b9411a5bc --- /dev/null +++ b/libc/stdio/fflush_unlocked.c @@ -0,0 +1,98 @@ +/*-*- 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/alg/arraylist.internal.h" +#include "libc/bits/bits.h" +#include "libc/bits/pushpop.h" +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/intrin/spinlock.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/fflush.internal.h" +#include "libc/stdio/internal.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/o.h" + +/** + * Blocks until data from stream buffer is written out. + * + * @param f is the stream handle, or 0 for all streams + * @return is 0 on success or -1 on error + */ +int fflush_unlocked(FILE *f) { + int rc = 0; + size_t i; + if (!f) { + _spinlock(&__fflush.lock); + for (i = __fflush.handles.i; i; --i) { + if ((f = __fflush.handles.p[i - 1])) { + if (fflush(f) == -1) { + rc = -1; + } + } + } + _spunlock(&__fflush.lock); + } else if (f->fd != -1) { + if (__fflush_impl(f) == -1) { + rc = -1; + } + } else if (f->beg && f->beg < f->size) { + f->buf[f->beg] = 0; + } + return rc; +} + +textstartup int __fflush_register(FILE *f) { + int rc; + size_t i; + struct StdioFlush *sf; + _spinlock(&__fflush.lock); + sf = &__fflush; + if (!sf->handles.p) { + sf->handles.p = sf->handles_initmem; + pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem)); + __cxa_atexit(fflush_unlocked, 0, 0); + } + for (i = sf->handles.i; i; --i) { + if (!sf->handles.p[i - 1]) { + sf->handles.p[i - 1] = f; + _spunlock(&__fflush.lock); + return 0; + } + } + rc = append(&sf->handles, &f); + _spunlock(&__fflush.lock); + return rc; +} + +void __fflush_unregister(FILE *f) { + size_t i; + struct StdioFlush *sf; + _spinlock(&__fflush.lock); + sf = &__fflush; + sf = pushpop(sf); + for (i = sf->handles.i; i; --i) { + if (sf->handles.p[i - 1] == f) { + pushmov(&sf->handles.p[i - 1], 0); + break; + } + } + _spunlock(&__fflush.lock); +} diff --git a/libc/stdio/fflushimpl.c b/libc/stdio/fflushimpl.c index 9d6ecea49..8805b9a76 100644 --- a/libc/stdio/fflushimpl.c +++ b/libc/stdio/fflushimpl.c @@ -18,12 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" #include "libc/sysv/consts/o.h" int __fflush_impl(FILE *f) { size_t i; ssize_t rc; + free_s(&f->getln); if (f->beg && !f->end && (f->iomode & O_ACCMODE) != O_RDONLY) { for (i = 0; i < f->beg; i += rc) { if ((rc = write(f->fd, f->buf + i, f->beg - i)) == -1) { diff --git a/libc/stdio/fgetln.c b/libc/stdio/fgetln.c index 6749197e3..94c5643c9 100644 --- a/libc/stdio/fgetln.c +++ b/libc/stdio/fgetln.c @@ -18,13 +18,35 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/** + * Retrieves line from stream, e.g. + * + * char *line; + * while ((line = _chomp(fgetln(stdin, 0)))) { + * printf("%s\n", line); + * } + * + * The returned memory is owned by the stream. It'll be reused when + * fgetln() is called again. It's free()'d upon fclose() / fflush() + * + * @param stream specifies non-null open input stream + * @param len optionally receives byte length of line + * @return nul-terminated line string, including the `\n` character + * unless a line happened before EOF without `\n`, otherwise it + * returns `NULL` and feof() and ferror() can examine the state + * @see getdelim() + */ char *fgetln(FILE *stream, size_t *len) { + char *res; ssize_t rc; size_t n = 0; - if ((rc = getdelim(&stream->getln, &n, '\n', stream)) > 0) { - *len = rc; - return stream->getln; + flockfile(stream); + if ((rc = getdelim_unlocked(&stream->getln, &n, '\n', stream)) > 0) { + if (len) *len = rc; + res = stream->getln; } else { - return 0; + res = 0; } + funlockfile(stream); + return res; } diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c index 223ead118..7a575904b 100644 --- a/libc/stdio/flockfile.c +++ b/libc/stdio/flockfile.c @@ -28,16 +28,18 @@ void flockfile(FILE *f) { int me, owner; unsigned tries; - if (!__threaded) return; - for (tries = 0, me = gettid();;) { - owner = 0; - if (_lockcmpxchgp(&f->lock, &owner, me) || owner == me) { - return; - } - if (++tries & 7) { - __builtin_ia32_pause(); - } else { - sched_yield(); + if (__threaded) { + for (tries = 0, me = gettid();;) { + owner = 0; + if (_lockcmpxchgp(&f->lock, &owner, me) || owner == me) { + break; + } + if (++tries & 7) { + __builtin_ia32_pause(); + } else { + sched_yield(); + } } } + ++f->reent; } diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c index 9b432718d..8aa5e1494 100644 --- a/libc/stdio/fputc.c +++ b/libc/stdio/fputc.c @@ -29,7 +29,7 @@ int fputc_unlocked(int c, FILE *f) { unsigned char b; if (c != '\n' && f->beg < f->size && f->bufmode != _IONBF) { f->buf[f->beg++] = c; - return c & 0xff; + return c & 255; } else { b = c; if (!fwrite_unlocked(&b, 1, 1, f)) return -1; diff --git a/libc/stdio/ftrylockfile.c b/libc/stdio/ftrylockfile.c index 20bffd3ca..eeed1e490 100644 --- a/libc/stdio/ftrylockfile.c +++ b/libc/stdio/ftrylockfile.c @@ -34,5 +34,8 @@ int ftrylockfile(FILE *f) { owner = 0; } } + if (!owner) { + ++f->reent; + } return owner; } diff --git a/libc/stdio/funlockfile.c b/libc/stdio/funlockfile.c index 46d68d0fd..0e1601a1c 100644 --- a/libc/stdio/funlockfile.c +++ b/libc/stdio/funlockfile.c @@ -16,12 +16,24 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/spinlock.h" +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" /** * Releases lock on stdio object. */ void funlockfile(FILE *f) { - _spunlock(&f->lock); + int owner; + bool shouldunlock; + assert(f->reent > 0); + shouldunlock = --f->reent <= 0; + if (__threaded) { + assert(f->lock == gettid()); + if (shouldunlock) { + owner = 0; + __atomic_store(&f->lock, &owner, __ATOMIC_RELAXED); + } + } } diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index fa29af049..1bfffabfb 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -16,64 +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/errno.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/o.h" - -static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) { - char *p; - ssize_t rc; - size_t i, m; - if ((f->iomode & O_ACCMODE) == O_WRONLY) { - f->state = errno = EBADF; - return -1; - } - if (f->beg > f->end) { - f->state = errno = EINVAL; - return -1; - } - if (!*s) *n = 0; - for (i = 0;; i += m) { - m = f->end - f->beg; - if ((p = memchr(f->buf + f->beg, delim, m))) m = p + 1 - (f->buf + f->beg); - if (i + m + 1 > *n) { - *n = i + m + 1; - *s = realloc(*s, *n); - if (!*s) { - abort(); - } - } - memcpy(*s + i, f->buf + f->beg, m); - (*s)[i + m] = '\0'; - if ((f->beg += m) == f->end) f->beg = f->end = 0; - if (p) { - return i + m; - } else if (f->fd == -1) { - break; - } else if ((rc = read(f->fd, f->buf, f->size)) != -1) { - if (!rc) break; - f->end = rc; - } else if (errno != EINTR) { - f->state = errno; - return -1; - } - } - f->state = -1; - if (i + m) { - return i + m; - } else { - return -1; - } -} /** - * Reads string from stream. + * Reads string from stream, e.g. + * + * char *line = NULL; + * size_t linesize = 0; + * while (getdelim(&line, &linesize, '\n', stdin) > 0) { + * _chomp(line); + * printf("%s\n", line); + * } + * free(line); * * @param s is the caller's buffer (in/out) which is extended or * allocated automatically, also NUL-terminated is guaranteed @@ -81,8 +35,9 @@ static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) { * @param delim is the stop char (and NUL is implicitly too) * @return number of bytes read >0, including delim, excluding NUL, * or -1 w/ errno on EOF or error; see ferror() and feof() - * @note this function can't punt EINTR to caller - * @see getline(), _chomp(), gettok_r() + * @note this function will ignore EINTR if it occurs mid-line + * @raises EBADF if stream isn't open for reading + * @see fgetln(), getline(), _chomp(), gettok_r() */ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) { ssize_t rc; diff --git a/libc/stdio/getdelim_unlocked.c b/libc/stdio/getdelim_unlocked.c new file mode 100644 index 000000000..3314c4379 --- /dev/null +++ b/libc/stdio/getdelim_unlocked.c @@ -0,0 +1,81 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" + +/** + * Reads string from unlocked stream. + * @see getdelim() for documentation + */ +ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) { + ssize_t rc; + char *p, *s2; + size_t i, m, n2; + if ((f->iomode & O_ACCMODE) == O_WRONLY) { + f->state = errno = EBADF; + return -1; + } + if (f->beg > f->end) { + f->state = errno = EINVAL; + return -1; + } + if (!*s) *n = 0; + for (i = 0;; i += m) { + m = f->end - f->beg; + if ((p = memchr(f->buf + f->beg, delim, m))) { + m = p + 1 - (f->buf + f->beg); + } + if (i + m + 1 > *n) { + n2 = i + m + 1; + s2 = realloc(*s, n2); + if (s2) { + *s = s2; + *n = n2; + } else { + f->state = errno; + return -1; + } + } + memcpy(*s + i, f->buf + f->beg, m); + (*s)[i + m] = '\0'; + if ((f->beg += m) == f->end) { + f->beg = f->end = 0; + } + if (p) { + return i + m; + } else if (f->fd == -1) { + break; + } else if ((rc = read(f->fd, f->buf, f->size)) != -1) { + if (!rc) break; + f->end = rc; + } else if (errno != EINTR) { + f->state = errno; + return -1; + } + } + f->state = -1; + if (i + m) { + return i + m; + } else { + return -1; + } +} diff --git a/libc/stdio/getline.c b/libc/stdio/getline.c index 6f0fd43b8..88f17836e 100644 --- a/libc/stdio/getline.c +++ b/libc/stdio/getline.c @@ -19,7 +19,15 @@ #include "libc/stdio/stdio.h" /** - * Reads line from stream. + * Reads line from stream, e.g. + * + * char *line = NULL; + * size_t linesize = 0; + * while (getline(&line, &linesize, stdin) > 0) { + * _chomp(line); + * printf("%s\n", line); + * } + * free(line); * * This function delegates to getdelim(), which provides further * documentation. Concerning lines, please note the \n or \r\n are @@ -30,7 +38,7 @@ * NUL-termination is guaranteed FTMP * @return number of bytes read, including delim, excluding NUL, or -1 * w/ errno on EOF or error; see ferror() and feof() - * @see xgetline(), getdelim(), gettok_r() + * @see fgetln(), xgetline(), getdelim(), gettok_r() */ ssize_t getline(char **line, size_t *n, FILE *f) { return getdelim(line, n, '\n', f); diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 9a903e999..f39c53b41 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -25,7 +25,8 @@ typedef struct FILE { uint32_t nofree; /* 0x24 */ int pid; /* 0x28 */ int lock; /* 0x2c */ - char *getln; /* 0x30 */ + int reent; /* 0x30 */ + char *getln; /* 0x38 */ } FILE; extern FILE *stdin; @@ -39,6 +40,7 @@ int getc(FILE *) paramsnonnull(); int putc(int, FILE *) paramsnonnull(); int fflush(FILE *); int fgetc(FILE *) paramsnonnull(); +char *fgetln(FILE *, size_t *) paramsnonnull((1)); int ungetc(int, FILE *) paramsnonnull(); int fileno(FILE *) paramsnonnull() nosideeffect; int fputc(int, FILE *) paramsnonnull(); @@ -120,9 +122,9 @@ int fwide(FILE *, int); │ cosmopolitan § standard i/o » without mutexes ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -void flockfile(FILE *); -void funlockfile(FILE *); -int ftrylockfile(FILE *); +void flockfile(FILE *) paramsnonnull(); +void funlockfile(FILE *) paramsnonnull(); +int ftrylockfile(FILE *) paramsnonnull(); int getc_unlocked(FILE *) paramsnonnull(); int getchar_unlocked(void); int putc_unlocked(int, FILE *) paramsnonnull(); @@ -149,6 +151,7 @@ int fputws_unlocked(const wchar_t *, FILE *); wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull(); int ungetc_unlocked(int, FILE *) paramsnonnull(); int fseeko_unlocked(FILE *, int64_t, int) paramsnonnull(); +ssize_t getdelim_unlocked(char **, size_t *, int, FILE *) paramsnonnull(); int fprintf_unlocked(FILE *, const char *, ...) printfesque(2) paramsnonnull((1, 2)) dontthrow nocallback; int vfprintf_unlocked(FILE *, const char *, va_list) diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index cd2ffc2a6..c2bab986f 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1573,18 +1573,6 @@ syscon sock SOCK_NONBLOCK 0x0800 0x0800 0x20000000 0x4000 0x20000000 syscon sock SOCK_DCCP 6 0 0 0 0 0 # what is it? syscon sock SOCK_PACKET 10 0 0 0 0 0 # what is it? -syscon prsnlty ADDR_COMPAT_LAYOUT 0x0200000 0 0 0 0 0 # linux only -syscon prsnlty READ_IMPLIES_EXEC 0x0400000 0 0 0 0 0 # linux only -syscon prsnlty ADDR_LIMIT_3GB 0x8000000 0 0 0 0 0 # linux only -syscon prsnlty FDPIC_FUNCPTRS 0x0080000 0 0 0 0 0 # linux only -syscon prsnlty STICKY_TIMEOUTS 0x4000000 0 0 0 0 0 # linux only -syscon prsnlty MMAP_PAGE_ZERO 0x0100000 0 0 0 0 0 # linux only -syscon prsnlty ADDR_LIMIT_32BIT 0x0800000 0 0 0 0 0 # linux only -syscon prsnlty WHOLE_SECONDS 0x2000000 0 0 0 0 0 # linux only -syscon prsnlty ADDR_NO_RANDOMIZE 0x0040000 0 0 0 0 0 # linux only -syscon prsnlty SHORT_INODE 0x1000000 0 0 0 0 0 # linux only -syscon prsnlty UNAME26 0x0020000 0 0 0 0 0 # linux only - syscon misc TH_FIN 1 1 1 1 1 1 # consensus syscon misc TH_SYN 2 2 2 2 2 2 # consensus syscon misc TH_RST 4 4 4 4 4 4 # consensus diff --git a/libc/sysv/consts/ADDR_COMPAT_LAYOUT.S b/libc/sysv/consts/ADDR_COMPAT_LAYOUT.S deleted file mode 100644 index a2613bfd9..000000000 --- a/libc/sysv/consts/ADDR_COMPAT_LAYOUT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,ADDR_COMPAT_LAYOUT,0x0200000,0,0,0,0,0 diff --git a/libc/sysv/consts/ADDR_LIMIT_32BIT.S b/libc/sysv/consts/ADDR_LIMIT_32BIT.S deleted file mode 100644 index 6ad2491dd..000000000 --- a/libc/sysv/consts/ADDR_LIMIT_32BIT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,ADDR_LIMIT_32BIT,0x0800000,0,0,0,0,0 diff --git a/libc/sysv/consts/ADDR_LIMIT_3GB.S b/libc/sysv/consts/ADDR_LIMIT_3GB.S deleted file mode 100644 index 6e29ba87c..000000000 --- a/libc/sysv/consts/ADDR_LIMIT_3GB.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,ADDR_LIMIT_3GB,0x8000000,0,0,0,0,0 diff --git a/libc/sysv/consts/ADDR_NO_RANDOMIZE.S b/libc/sysv/consts/ADDR_NO_RANDOMIZE.S deleted file mode 100644 index 3a076ae64..000000000 --- a/libc/sysv/consts/ADDR_NO_RANDOMIZE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,ADDR_NO_RANDOMIZE,0x0040000,0,0,0,0,0 diff --git a/libc/sysv/consts/FDPIC_FUNCPTRS.S b/libc/sysv/consts/FDPIC_FUNCPTRS.S deleted file mode 100644 index 4140c78ca..000000000 --- a/libc/sysv/consts/FDPIC_FUNCPTRS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,FDPIC_FUNCPTRS,0x0080000,0,0,0,0,0 diff --git a/libc/sysv/consts/MMAP_PAGE_ZERO.S b/libc/sysv/consts/MMAP_PAGE_ZERO.S deleted file mode 100644 index 44a90993a..000000000 --- a/libc/sysv/consts/MMAP_PAGE_ZERO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,MMAP_PAGE_ZERO,0x0100000,0,0,0,0,0 diff --git a/libc/sysv/consts/READ_IMPLIES_EXEC.S b/libc/sysv/consts/READ_IMPLIES_EXEC.S deleted file mode 100644 index c2a749151..000000000 --- a/libc/sysv/consts/READ_IMPLIES_EXEC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,READ_IMPLIES_EXEC,0x0400000,0,0,0,0,0 diff --git a/libc/sysv/consts/SHORT_INODE.S b/libc/sysv/consts/SHORT_INODE.S deleted file mode 100644 index 4605ea47e..000000000 --- a/libc/sysv/consts/SHORT_INODE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,SHORT_INODE,0x1000000,0,0,0,0,0 diff --git a/libc/sysv/consts/STICKY_TIMEOUTS.S b/libc/sysv/consts/STICKY_TIMEOUTS.S deleted file mode 100644 index e15c5f294..000000000 --- a/libc/sysv/consts/STICKY_TIMEOUTS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,STICKY_TIMEOUTS,0x4000000,0,0,0,0,0 diff --git a/libc/sysv/consts/UNAME26.S b/libc/sysv/consts/UNAME26.S deleted file mode 100644 index 7288894b2..000000000 --- a/libc/sysv/consts/UNAME26.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,UNAME26,0x0020000,0,0,0,0,0 diff --git a/libc/sysv/consts/WHOLE_SECONDS.S b/libc/sysv/consts/WHOLE_SECONDS.S deleted file mode 100644 index 161ad743f..000000000 --- a/libc/sysv/consts/WHOLE_SECONDS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon prsnlty,WHOLE_SECONDS,0x2000000,0,0,0,0,0 diff --git a/libc/sysv/consts/personality.h b/libc/sysv/consts/personality.h index 432f53a0e..00b60d6c6 100644 --- a/libc/sysv/consts/personality.h +++ b/libc/sysv/consts/personality.h @@ -1,34 +1,17 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_PERSONALITY_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_PERSONALITY_H_ #include "libc/runtime/symbolic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -extern const long ADDR_COMPAT_LAYOUT; -extern const long READ_IMPLIES_EXEC; -extern const long ADDR_LIMIT_3GB; -extern const long FDPIC_FUNCPTRS; -extern const long STICKY_TIMEOUTS; -extern const long MMAP_PAGE_ZERO; -extern const long ADDR_LIMIT_32BIT; -extern const long WHOLE_SECONDS; -extern const long ADDR_NO_RANDOMIZE; -extern const long SHORT_INODE; -extern const long UNAME26; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ - -#define ADDR_COMPAT_LAYOUT SYMBOLIC(ADDR_COMPAT_LAYOUT) -#define READ_IMPLIES_EXEC SYMBOLIC(READ_IMPLIES_EXEC) -#define ADDR_LIMIT_3GB SYMBOLIC(ADDR_LIMIT_3GB) -#define FDPIC_FUNCPTRS SYMBOLIC(FDPIC_FUNCPTRS) -#define STICKY_TIMEOUTS SYMBOLIC(STICKY_TIMEOUTS) -#define MMAP_PAGE_ZERO SYMBOLIC(MMAP_PAGE_ZERO) -#define ADDR_LIMIT_32BIT SYMBOLIC(ADDR_LIMIT_32BIT) -#define WHOLE_SECONDS SYMBOLIC(WHOLE_SECONDS) -#define ADDR_NO_RANDOMIZE SYMBOLIC(ADDR_NO_RANDOMIZE) -#define SHORT_INODE SYMBOLIC(SHORT_INODE) -#define UNAME26 SYMBOLIC(UNAME26) +#define ADDR_COMPAT_LAYOUT 0x0200000 +#define READ_IMPLIES_EXEC 0x0400000 +#define ADDR_LIMIT_3GB 0x8000000 +#define FDPIC_FUNCPTRS 0x0080000 +#define STICKY_TIMEOUTS 0x4000000 +#define MMAP_PAGE_ZERO 0x0100000 +#define ADDR_LIMIT_32BIT 0x0800000 +#define WHOLE_SECONDS 0x2000000 +#define ADDR_NO_RANDOMIZE 0x0040000 +#define SHORT_INODE 0x1000000 +#define UNAME26 0x0020000 #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_PERSONALITY_H_ */ diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index 6362619f0..afec87b92 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -55,6 +55,18 @@ TEST(open, enametoolong) { ASSERT_SYS(ENAMETOOLONG, -1, creat(s, 0644)); } +TEST(open, testSpaceInFilename) { + char buf[8] = {0}; + ASSERT_SYS(0, 0, xbarf("hello txt", "hello", -1)); + ASSERT_SYS(0, 3, open("hello txt", O_WRONLY)); + EXPECT_SYS(0, 1, write(3, "H", 1)); + EXPECT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, open("hello txt", O_RDONLY)); + EXPECT_SYS(0, 5, read(3, buf, 7)); + EXPECT_STREQ("Hello", buf); + EXPECT_SYS(0, 0, close(3)); +} + TEST(open, testOpenExistingForWriteOnly_seeksToStart) { char buf[8] = {0}; ASSERT_SYS(0, 0, xbarf("hello.txt", "hello", -1)); diff --git a/test/libc/intrin/asan_test.c b/test/libc/intrin/asan_test.c index 46bb2c0ea..1caf76766 100644 --- a/test/libc/intrin/asan_test.c +++ b/test/libc/intrin/asan_test.c @@ -46,6 +46,27 @@ TEST(asan, test) { EXPECT_FALSE(__asan_is_valid(p, 64 + 4)); EXPECT_TRUE(__asan_is_valid(p + 1, 64 + 2)); EXPECT_FALSE(__asan_is_valid(p + 1, 64 + 3)); + EXPECT_FALSE(__asan_is_valid(p - 1, 64)); +} + +TEST(asan, test2) { + char *p; + if (!IsAsan()) return; + p = gc(memalign(16, 64)); + // asan design precludes this kind of poisoning + __asan_poison(p + 1, 1, kAsanProtected); + EXPECT_TRUE(__asan_is_valid(p, 2)); + EXPECT_TRUE(__asan_is_valid(p + 1, 2)); + // but we can do this + __asan_poison(p + 7, 1, kAsanProtected); + EXPECT_TRUE(__asan_is_valid(p + 6, 1)); + EXPECT_FALSE(__asan_is_valid(p + 7, 1)); + EXPECT_TRUE(__asan_is_valid(p + 8, 1)); + EXPECT_FALSE(__asan_is_valid(p + 6, 2)); + EXPECT_FALSE(__asan_is_valid(p + 7, 2)); + EXPECT_FALSE(__asan_is_valid(p + 6, 3)); + __asan_unpoison(p + 7, 1); + EXPECT_TRUE(__asan_is_valid(p + 6, 3)); } TEST(asan, testEmptySize_isAlwaysValid) { diff --git a/test/libc/intrin/gettid_test.c b/test/libc/intrin/gettid_test.c index e69de29bb..5f7bde5b8 100644 --- a/test/libc/intrin/gettid_test.c +++ b/test/libc/intrin/gettid_test.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 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/dce.h" +#include "libc/nexgen32e/threaded.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char tib[64]; + +TEST(gettid, test) { + if (IsLinux()) EXPECT_EQ(getpid(), gettid()); + if (IsNetbsd()) EXPECT_EQ(1, gettid()); +} + +BENCH(gettid, bench) { + int gettid_(void) asm("gettid"); + EZBENCH2("gettid (single threaded)", donothing, gettid()); + __install_tls(__initialize_tls(tib)); + EZBENCH2("gettid (tls enabled)", donothing, gettid()); +} diff --git a/test/libc/intrin/tls_test.c b/test/libc/intrin/tls_test.c index 42d1136c4..00d64c73a 100644 --- a/test/libc/intrin/tls_test.c +++ b/test/libc/intrin/tls_test.c @@ -28,6 +28,8 @@ TEST(tls, test) { EXPECT_EQ(31337, errno); EXPECT_EQ(&__errno, __errno_location()); __initialize_tls(tib); + *(int *)((char *)tib + 0x38) = gettid(); + *(int *)((char *)tib + 0x3c) = __errno; __install_tls(tib); EXPECT_EQ(31337, errno); EXPECT_EQ(tib, __get_tls()); diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index f57b7d516..d406a6abc 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -80,10 +80,10 @@ TEST(rand64, testLcg_doesntProduceIdenticalValues) { } TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { - char *tls[THREADS]; + int i, j, rc, ws; sigset_t ss, oldss; + char *tls[THREADS]; void *stacks[THREADS]; - int i, j, rc, ws, tid[THREADS]; struct sigaction oldsa; struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART}; EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa)); @@ -92,19 +92,20 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); ready = false; - memset(tid, -1, sizeof(tid)); for (i = 0; i < THREADS; ++i) { tls[i] = __initialize_tls(calloc(1, 64)); stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(-1, clone(Thrasher, stacks[i], GetStackSize(), - CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_CLEARTID, - (void *)(intptr_t)i, 0, tls[i], 64, tid + i)); + ASSERT_NE( + -1, + clone(Thrasher, stacks[i], GetStackSize(), + CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_SETTLS | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, + (void *)(intptr_t)i, 0, tls[i], 64, (int *)(tls[i] + 0x38))); } ready = true; for (i = 0; i < THREADS; ++i) { - _spinlock(tid + i); + _spinlock((int *)(tls[i] + 0x38)); EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize())); free(tls[i]); } diff --git a/test/libc/runtime/ape_test.c b/test/libc/runtime/ape_test.c index 9f07cd69f..fb140b008 100644 --- a/test/libc/runtime/ape_test.c +++ b/test/libc/runtime/ape_test.c @@ -41,6 +41,12 @@ void Extract(const char *from, const char *to, int mode) { } void SetUpOnce(void) { + + // nothing to do if we're using elf + if (~SUPPORT_VECTOR & (WINDOWS | XNU)) { + exit(0); + } + ASSERT_SYS(0, 0, mkdir("bin", 0755)); Extract("/zip/apetest.com", "bin/apetest.com", 0755); Extract("/zip/apetest2.com", "bin/apetest2.com", 0755); diff --git a/test/libc/runtime/clone_test.c b/test/libc/runtime/clone_test.c index fda45026a..8c92f9ec6 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -45,7 +45,7 @@ void SetUp(void) { tls = calloc(1, 64); __initialize_tls(tls); *(int *)(tls + 0x3c) = 31337; - childetid = (int *)(tls + 0x0038); + childetid = (int *)(tls + 0x38); ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0))); } @@ -95,6 +95,7 @@ TEST(clone, test1) { CLONE_SETTLS, (void *)23, &ptid, tls, 64, childetid))); _spinlock(childetid); // CLONE_CHILD_CLEARTID + ASSERT_NE(gettid(), tid); ASSERT_EQ(tid, ptid); ASSERT_EQ(42, x); ASSERT_NE(me, tid); @@ -130,6 +131,6 @@ TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { BENCH(clone, bench) { errno_t *volatile ep; char *volatile tp; - EZBENCH2("__errno_location", donothing, (ep = (__errno_location()))); + EZBENCH2("__errno_location", donothing, (ep = __errno_location())); EZBENCH2("__get_tls", donothing, (tp = __get_tls())); } diff --git a/test/libc/stdio/fgetln_test.c b/test/libc/stdio/fgetln_test.c new file mode 100644 index 000000000..7cba463c6 --- /dev/null +++ b/test/libc/stdio/fgetln_test.c @@ -0,0 +1,87 @@ +/*-*- 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/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(fgetln, test) { + FILE *f; + f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+"); + EXPECT_STREQ("The fall of Hyperion - a Dream\n", fgetln(f, 0)); + EXPECT_STREQ("John Keats\n", fgetln(f, 0)); + EXPECT_STREQ("\n", fgetln(f, 0)); + fclose(f); +} + +TEST(fgetln, testGoodLastLine) { + FILE *f; + size_t n; + f = fmemopen("foo\nbar\n", 8, "r"); + EXPECT_STREQ("foo\n", fgetln(f, &n)); + EXPECT_EQ(4, n); + EXPECT_STREQ("bar\n", fgetln(f, &n)); + EXPECT_EQ(4, n); + EXPECT_EQ(NULL, fgetln(f, 0)); + EXPECT_FALSE(ferror(f)); + EXPECT_TRUE(feof(f)); + fclose(f); +} + +TEST(fgetln, testEvilLastLine) { + FILE *f; + f = fmemopen("foo\nbar", 7, "r"); + EXPECT_STREQ("foo\n", fgetln(f, 0)); + EXPECT_STREQ("bar", fgetln(f, 0)); + EXPECT_EQ(NULL, fgetln(f, 0)); + EXPECT_FALSE(ferror(f)); + EXPECT_TRUE(feof(f)); + fclose(f); +} + +TEST(fgetln, testReadingFromStdin_doesntLeakMemory) { + FILE *f; + int oldstdin, pfds[2]; + oldstdin = dup(0); + EXPECT_SYS(0, 0, pipe(pfds)); + EXPECT_SYS(0, 8, write(pfds[1], "foo\nbar\n", 8)); + EXPECT_SYS(0, 0, close(pfds[1])); + EXPECT_SYS(0, 0, close(0)); + EXPECT_SYS(0, 0, dup(pfds[0])); + EXPECT_SYS(0, 0, close(pfds[0])); + EXPECT_STREQ("foo\n", fgetln(stdin, 0)); + EXPECT_STREQ("bar\n", fgetln(stdin, 0)); + EXPECT_EQ(NULL, fgetln(stdin, 0)); + EXPECT_FALSE(ferror(stdin)); + EXPECT_TRUE(feof(stdin)); + EXPECT_SYS(0, 0, close(0)); + EXPECT_SYS(0, 0, dup(oldstdin)); + clearerr(stdin); +} + +BENCH(fgetln, bench) { + FILE *f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+"); + EZBENCH2("fgetln", donothing, fgetln(f, 0)); + EZBENCH2("xgetline", donothing, free(xgetline(f))); + fclose(f); +} diff --git a/third_party/libcxx/errno.h b/third_party/libcxx/errno.h index 00d7f7f83..1dfe334b6 100644 --- a/third_party/libcxx/errno.h +++ b/third_party/libcxx/errno.h @@ -78,320 +78,6 @@ static const int __elast2 = 105; // supply errno values likely to be missing, particularly on Windows -#ifndef EAFNOSUPPORT -#define EAFNOSUPPORT 9901 -#endif - -#ifndef EADDRINUSE -#define EADDRINUSE 9902 -#endif - -#ifndef EADDRNOTAVAIL -#define EADDRNOTAVAIL 9903 -#endif - -#ifndef EISCONN -#define EISCONN 9904 -#endif - -#ifndef EBADMSG -#define EBADMSG 9905 -#endif - -#ifndef ECONNABORTED -#define ECONNABORTED 9906 -#endif - -#ifndef EALREADY -#define EALREADY 9907 -#endif - -#ifndef ECONNREFUSED -#define ECONNREFUSED 9908 -#endif - -#ifndef ECONNRESET -#define ECONNRESET 9909 -#endif - -#ifndef EDESTADDRREQ -#define EDESTADDRREQ 9910 -#endif - -#ifndef EHOSTUNREACH -#define EHOSTUNREACH 9911 -#endif - -#ifndef EIDRM -#define EIDRM 9912 -#endif - -#ifndef EMSGSIZE -#define EMSGSIZE 9913 -#endif - -#ifndef ENETDOWN -#define ENETDOWN 9914 -#endif - -#ifndef ENETRESET -#define ENETRESET 9915 -#endif - -#ifndef ENETUNREACH -#define ENETUNREACH 9916 -#endif - -#ifndef ENOBUFS -#define ENOBUFS 9917 -#endif - -#ifndef ENOLINK -#define ENOLINK 9918 -#endif - -#ifndef ENODATA -#define ENODATA 9919 -#endif - -#ifndef ENOMSG -#define ENOMSG 9920 -#endif - -#ifndef ENOPROTOOPT -#define ENOPROTOOPT 9921 -#endif - -#ifndef ENOSR -#define ENOSR 9922 -#endif - -#ifndef ENOTSOCK -#define ENOTSOCK 9923 -#endif - -#ifndef ENOSTR -#define ENOSTR 9924 -#endif - -#ifndef ENOTCONN -#define ENOTCONN 9925 -#endif - -#ifndef ENOTSUP -#define ENOTSUP 9926 -#endif - -#ifndef ECANCELED -#define ECANCELED 9927 -#endif - -#ifndef EINPROGRESS -#define EINPROGRESS 9928 -#endif - -#ifndef EOPNOTSUPP -#define EOPNOTSUPP 9929 -#endif - -#ifndef EWOULDBLOCK -#define EWOULDBLOCK 9930 -#endif - -#ifndef EOWNERDEAD -#define EOWNERDEAD 9931 -#endif - -#ifndef EPROTO -#define EPROTO 9932 -#endif - -#ifndef EPROTONOSUPPORT -#define EPROTONOSUPPORT 9933 -#endif - -#ifndef ENOTRECOVERABLE -#define ENOTRECOVERABLE 9934 -#endif - -#ifndef ETIME -#define ETIME 9935 -#endif - -#ifndef ETXTBSY -#define ETXTBSY 9936 -#endif - -#ifndef ETIMEDOUT -#define ETIMEDOUT 9938 -#endif - -#ifndef ELOOP -#define ELOOP 9939 -#endif - -#ifndef EOVERFLOW -#define EOVERFLOW 9940 -#endif - -#ifndef EPROTOTYPE -#define EPROTOTYPE 9941 -#endif - -#ifndef ENOSYS -#define ENOSYS 9942 -#endif - -#ifndef EINVAL -#define EINVAL 9943 -#endif - -#ifndef ERANGE -#define ERANGE 9944 -#endif - -#ifndef EILSEQ -#define EILSEQ 9945 -#endif - -// Windows Mobile doesn't appear to define these: - -#ifndef E2BIG -#define E2BIG 9946 -#endif - -#ifndef EDOM -#define EDOM 9947 -#endif - -#ifndef EFAULT -#define EFAULT 9948 -#endif - -#ifndef EBADF -#define EBADF 9949 -#endif - -#ifndef EPIPE -#define EPIPE 9950 -#endif - -#ifndef EXDEV -#define EXDEV 9951 -#endif - -#ifndef EBUSY -#define EBUSY 9952 -#endif - -#ifndef ENOTEMPTY -#define ENOTEMPTY 9953 -#endif - -#ifndef ENOEXEC -#define ENOEXEC 9954 -#endif - -#ifndef EEXIST -#define EEXIST 9955 -#endif - -#ifndef EFBIG -#define EFBIG 9956 -#endif - -#ifndef ENAMETOOLONG -#define ENAMETOOLONG 9957 -#endif - -#ifndef ENOTTY -#define ENOTTY 9958 -#endif - -#ifndef EINTR -#define EINTR 9959 -#endif - -#ifndef ESPIPE -#define ESPIPE 9960 -#endif - -#ifndef EIO -#define EIO 9961 -#endif - -#ifndef EISDIR -#define EISDIR 9962 -#endif - -#ifndef ECHILD -#define ECHILD 9963 -#endif - -#ifndef ENOLCK -#define ENOLCK 9964 -#endif - -#ifndef ENOSPC -#define ENOSPC 9965 -#endif - -#ifndef ENXIO -#define ENXIO 9966 -#endif - -#ifndef ENODEV -#define ENODEV 9967 -#endif - -#ifndef ENOENT -#define ENOENT 9968 -#endif - -#ifndef ESRCH -#define ESRCH 9969 -#endif - -#ifndef ENOTDIR -#define ENOTDIR 9970 -#endif - -#ifndef ENOMEM -#define ENOMEM 9971 -#endif - -#ifndef EPERM -#define EPERM 9972 -#endif - -#ifndef EACCES -#define EACCES 9973 -#endif - -#ifndef EROFS -#define EROFS 9974 -#endif - -#ifndef EDEADLK -#define EDEADLK 9975 -#endif - -#ifndef EAGAIN -#define EAGAIN 9976 -#endif - -#ifndef ENFILE -#define ENFILE 9977 -#endif - -#ifndef EMFILE -#define EMFILE 9978 -#endif - -#ifndef EMLINK -#define EMLINK 9979 -#endif - #endif // __cplusplus #endif // _LIBCPP_ERRNO_H diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 33eb69ac4..c579add12 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -234,9 +234,9 @@ static ssize_t pushline (lua_State *L, int firstline) { ssize_t rc; char *prmt; globalL = L; + prmt = strdup(get_prompt(L, firstline)); + lua_pop(L, 1); /* remove prompt */ if (lua_repl_isterminal) { - prmt = strdup(get_prompt(L, firstline)); - lua_pop(L, 1); /* remove prompt */ LUA_REPL_UNLOCK; rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); free(prmt); @@ -250,9 +250,17 @@ static ssize_t pushline (lua_State *L, int firstline) { LUA_REPL_LOCK; } else { LUA_REPL_UNLOCK; + fputs(prmt, stdout); + fflush(stdout); b = linenoiseGetLine(stdin); + if (b) { + rc = 1; + } else if (ferror(stdin)) { + rc = -1; + } else { + rc = 0; + } LUA_REPL_LOCK; - rc = b ? 1 : -1; } if (!(rc == -1 && errno == EAGAIN)) { write(1, "\n", 1); diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 73818ea0f..9a0ed28fc 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -307,7 +307,8 @@ static void doREPL (lua_State *L) { progname = NULL; /* no 'progname' on errors in interactive mode */ lua_initrepl(L, LUA_PROGNAME); for (;;) { - linenoiseEnableRawMode(0); + if (lua_repl_isterminal) + linenoiseEnableRawMode(0); TryAgain: status = lua_loadline(L); if (status == -2 && errno == EAGAIN) { @@ -315,7 +316,8 @@ static void doREPL (lua_State *L) { poll(&(struct pollfd){0, POLLIN}, 1, -1); goto TryAgain; } - linenoiseDisableRawMode(); + if (lua_repl_isterminal) + linenoiseDisableRawMode(); if (status == -1) { break; } else if (status == -2) { diff --git a/third_party/make/error.c b/third_party/make/error.c index 07bb08918..5fe39d0cc 100644 --- a/third_party/make/error.c +++ b/third_party/make/error.c @@ -57,48 +57,9 @@ void (*error_print_progname) (void); /* This variable is incremented each time 'error' is called. */ unsigned int error_message_count; -#ifdef _LIBC -/* In the GNU C library, there is a predefined variable for this. */ - -# define program_name program_invocation_name - -/* In GNU libc we want do not want to use the common name 'error' directly. - Instead make it a weak alias. */ -extern void __error (int status, int errnum, const char *message, ...) - __attribute__ ((__format__ (__printf__, 3, 4))); -extern void __error_at_line (int status, int errnum, const char *file_name, - unsigned int line_number, const char *message, - ...) - __attribute__ ((__format__ (__printf__, 5, 6))); -# define error __error -# define error_at_line __error_at_line - -# define fflush(s) _IO_fflush (s) -# undef putc -# define putc(c, fp) _IO_putc (c, fp) - - -#else /* not _LIBC */ - -# if defined _WIN32 && ! defined __CYGWIN__ -/* Get declarations of the native Windows API functions. */ -# define WIN32_LEAN_AND_MEAN -/* Get _get_osfhandle. */ -# if GNULIB_MSVC_NOTHROW -# include "msvc-nothrow.h" -# else -# endif -# endif - -/* The gnulib override of fcntl is not needed in this file. */ -# undef fcntl - -# define program_name getprogname () - -# if GNULIB_STRERROR_R_POSIX || HAVE_STRERROR_R || defined strerror_r -# define __strerror_r strerror_r -# endif /* GNULIB_STRERROR_R_POSIX || HAVE_STRERROR_R || defined strerror_r */ -#endif /* not _LIBC */ +#undef fcntl +#define program_name getprogname () +#define __strerror_r strerror_r /* Return non-zero if FD is open. */ static int diff --git a/third_party/python/Lib/test/test_doctest.py b/third_party/python/Lib/test/test_doctest.py index 6c870f0b9..d20718c4b 100644 --- a/third_party/python/Lib/test/test_doctest.py +++ b/third_party/python/Lib/test/test_doctest.py @@ -2921,7 +2921,7 @@ Invalid file name: >>> print(normalize(err)) # doctest: +ELLIPSIS Traceback (most recent call last): ... - FileNotFoundError: [Errno 2] ENOENT/2/No such file or directory: 'nosuchfile' + FileNotFoundError: [Errno 2] ENOENT... Invalid doctest option: diff --git a/third_party/python/Lib/test/test_module.py b/third_party/python/Lib/test/test_module.py index 2fbe9f1ce..81dccd2e8 100644 --- a/third_party/python/Lib/test/test_module.py +++ b/third_party/python/Lib/test/test_module.py @@ -29,7 +29,7 @@ class ModuleTests(unittest.TestCase): self.fail("__name__ = %s" % repr(s)) except AttributeError: pass - if cosmo.MODE != 'tiny': + if 'tiny' not in cosmo.MODE: self.assertEqual(foo.__doc__, ModuleType.__doc__) def test_uninitialized_missing_getattr(self): diff --git a/third_party/python/Modules/socketmodule.c b/third_party/python/Modules/socketmodule.c index 81bef83d5..4f5234fa1 100644 --- a/third_party/python/Modules/socketmodule.c +++ b/third_party/python/Modules/socketmodule.c @@ -12,6 +12,7 @@ #include "libc/dns/ent.h" #include "libc/errno.h" #include "libc/nt/enum/version.h" +#include "libc/nt/version.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" @@ -7146,9 +7147,9 @@ PyInit__socket(void) if (TCP_USER_TIMEOUT) PyModule_AddIntMacro(m, TCP_USER_TIMEOUT); if (TCP_SAVE_SYN) PyModule_AddIntMacro(m, TCP_SAVE_SYN); if (TCP_SAVED_SYN) PyModule_AddIntMacro(m, TCP_SAVED_SYN); - if (TCP_KEEPCNT && (!IsWindows() || NtGetVersion() >= kNtVersionWindows10)) + if (TCP_KEEPCNT && (!IsWindows() || IsAtLeastWindows10())) PyModule_AddIntMacro(m, TCP_KEEPCNT); - if (TCP_FASTOPEN && (!IsWindows() || NtGetVersion() >= kNtVersionWindows10)) + if (TCP_FASTOPEN && (!IsWindows() || IsAtLeastWindows10())) PyModule_AddIntMacro(m, TCP_FASTOPEN); if (TCP_FASTOPEN_CONNECT) PyModule_AddIntMacro(m, TCP_FASTOPEN_CONNECT); diff --git a/tool/net/help.txt b/tool/net/help.txt index 15814bd41..5a2630317 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -2791,6 +2791,9 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) + `intns` needs to be on the interval `[0,1000000000)` + `valuens` needs to be on the interval `[0,1000000000)` + unix.strsignal(sig:int) → str Turns platform-specific `sig` code into its symbolic name. diff --git a/tool/net/redbean.c b/tool/net/redbean.c index bf44a8800..03967ce58 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -16,151 +16,92 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "dsp/scale/cdecimate2xuint8x8.h" #include "libc/bits/atomic.h" -#include "libc/bits/bits.h" -#include "libc/bits/likely.h" -#include "libc/bits/popcnt.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" #include "libc/calls/math.h" #include "libc/calls/sigbits.h" -#include "libc/calls/strace.internal.h" -#include "libc/calls/struct/dirent.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/flock.h" +#include "libc/calls/struct/iovec.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/termios.h" -#include "libc/calls/ttydefaults.h" -#include "libc/dce.h" #include "libc/dns/dns.h" #include "libc/dns/hoststxt.h" #include "libc/dos.h" -#include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" -#include "libc/intrin/spinlock.h" -#include "libc/log/backtrace.internal.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/macros.internal.h" #include "libc/math.h" #include "libc/mem/alloca.h" -#include "libc/mem/fmt.h" -#include "libc/mem/mem.h" -#include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/rdtsc.h" -#include "libc/nexgen32e/rdtscp.h" #include "libc/nexgen32e/threaded.h" +#include "libc/nexgen32e/x86feature.h" #include "libc/nt/enum/fileflagandattributes.h" -#include "libc/nt/runtime.h" #include "libc/nt/thread.h" -#include "libc/nt/version.h" #include "libc/rand/rand.h" #include "libc/runtime/clktck.h" -#include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/gc.internal.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" -#include "libc/runtime/symbols.internal.h" #include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" #include "libc/stdio/hex.internal.h" -#include "libc/stdio/stdio.h" #include "libc/str/slice.h" -#include "libc/str/str.h" #include "libc/str/undeflate.h" #include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/audit.h" -#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/f.h" -#include "libc/sysv/consts/grnd.h" #include "libc/sysv/consts/inaddr.h" #include "libc/sysv/consts/ipproto.h" -#include "libc/sysv/consts/lock.h" -#include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" -#include "libc/sysv/consts/msync.h" -#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/pr.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rusage.h" -#include "libc/sysv/consts/s.h" #include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/shut.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/sysv/consts/termios.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" -#include "libc/testlib/testlib.h" -#include "libc/time/time.h" #include "libc/x/x.h" #include "libc/zip.h" #include "net/http/escape.h" #include "net/http/http.h" #include "net/http/ip.h" -#include "net/http/url.h" #include "net/https/https.h" #include "third_party/getopt/getopt.h" -#include "third_party/linenoise/linenoise.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lrepl.h" -#include "third_party/lua/ltests.h" -#include "third_party/lua/lua.h" -#include "third_party/lua/luaconf.h" #include "third_party/lua/lualib.h" -#include "third_party/mbedtls/asn1.h" -#include "third_party/mbedtls/asn1write.h" -#include "third_party/mbedtls/cipher.h" -#include "third_party/mbedtls/config.h" #include "third_party/mbedtls/ctr_drbg.h" #include "third_party/mbedtls/debug.h" -#include "third_party/mbedtls/ecp.h" -#include "third_party/mbedtls/entropy.h" -#include "third_party/mbedtls/entropy_poll.h" -#include "third_party/mbedtls/error.h" #include "third_party/mbedtls/iana.h" -#include "third_party/mbedtls/md.h" -#include "third_party/mbedtls/md5.h" #include "third_party/mbedtls/net_sockets.h" #include "third_party/mbedtls/oid.h" -#include "third_party/mbedtls/pk.h" -#include "third_party/mbedtls/rsa.h" #include "third_party/mbedtls/san.h" -#include "third_party/mbedtls/sha1.h" #include "third_party/mbedtls/ssl.h" #include "third_party/mbedtls/ssl_ticket.h" #include "third_party/mbedtls/x509.h" #include "third_party/mbedtls/x509_crt.h" -#include "third_party/regex/regex.h" #include "third_party/zlib/zlib.h" #include "tool/args/args.h" #include "tool/build/lib/case.h" -#include "tool/build/lib/psk.h" #include "tool/net/lfuncs.h" #include "tool/net/luacheck.h" #include "tool/net/sandbox.h" @@ -441,12 +382,14 @@ static int mainpid; static int sandboxed; static int changeuid; static int changegid; -static int monitortid; static int isyielding; static int statuscode; static int shutdownsig; static int sslpskindex; static int oldloglevel; +static int *monitortid; +static char *monitortls; +static char *monitorstack; static int maxpayloadsize; static int messageshandled; static int sslticketlifetime; @@ -6393,7 +6336,7 @@ static int ExitWorker(void) { } if (monitortty) { terminatemonitor = true; - _spinlock(&monitortid); + _spinlock(monitortid); } _Exit(0); } @@ -6649,19 +6592,23 @@ static int MemoryMonitor(void *arg) { } static void MonitorMemory(void) { - char *tls; - monitortid = -1; - tls = __initialize_tls(malloc(64)); - __cxa_atexit(free, tls, 0); - if (clone(MemoryMonitor, - mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, - MAP_STACK | MAP_ANONYMOUS, -1, 0), - GetStackSize(), - CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - CLONE_SETTLS | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, - 0, 0, tls, 64, &monitortid) == -1) { - monitortid = 0; + if ((monitortls = __initialize_tls(malloc(64)))) { + if ((monitorstack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { + monitortid = (int *)(monitortls + 0x38); + if ((*monitortid = + clone(MemoryMonitor, monitorstack, GetStackSize(), + CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS | CLONE_CHILD_CLEARTID, + 0, 0, monitortls, 64, monitortid)) != -1) { + return; + } + munmap(monitorstack, GetStackSize()); + } + free(monitortls); } + WARNF("(memv) failed to start memory monitor %m"); + monitortty = 0; } static int HandleConnection(size_t i) { @@ -7327,7 +7274,9 @@ void RedBean(int argc, char *argv[]) { } if (monitortty) { terminatemonitor = true; - _spinlock(&monitortid); + _spinlock(monitortid); + munmap(monitorstack, GetStackSize()); + free(monitortls); } INFOF("(srvr) shutdown complete"); } From e8632cfc3065a3eb9fbc2fcc1f7062f5bab7e683 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 23 May 2022 13:30:16 -0700 Subject: [PATCH 33/40] Revert recent change to ASAN module --- libc/intrin/asan.c | 77 +++++++++++++++++++++++++++++++++++++++++-- libc/intrin/intrin.mk | 3 -- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 93f63602a..664f7e396 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -257,6 +257,79 @@ static void __asan_memset(void *p, char c, size_t n) { } } +static void *__asan_mempcpy(void *dst, const void *src, size_t n) { + size_t i; + char *d; + const char *s; + uint64_t a, b; + d = dst; + s = src; + switch (n) { + case 0: + return d; + case 1: + *d = *s; + return d + 1; + case 2: + __builtin_memcpy(&a, s, 2); + __builtin_memcpy(d, &a, 2); + return d + 2; + case 3: + __builtin_memcpy(&a, s, 2); + __builtin_memcpy(&b, s + 1, 2); + __builtin_memcpy(d, &a, 2); + __builtin_memcpy(d + 1, &b, 2); + return d + 3; + case 4: + __builtin_memcpy(&a, s, 4); + __builtin_memcpy(d, &a, 4); + return d + 4; + case 5: + case 6: + case 7: + __builtin_memcpy(&a, s, 4); + __builtin_memcpy(&b, s + n - 4, 4); + __builtin_memcpy(d, &a, 4); + __builtin_memcpy(d + n - 4, &b, 4); + return d + n; + case 8: + __builtin_memcpy(&a, s, 8); + __builtin_memcpy(d, &a, 8); + return d + 8; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + __builtin_memcpy(&a, s, 8); + __builtin_memcpy(&b, s + n - 8, 8); + __builtin_memcpy(d, &a, 8); + __builtin_memcpy(d + n - 8, &b, 8); + return d + n; + default: + if (n <= 64) { + i = 0; + do { + __builtin_memcpy(&a, s + i, 8); + asm volatile("" ::: "memory"); + __builtin_memcpy(d + i, &a, 8); + } while ((i += 8) + 8 <= n); + for (; i < n; ++i) d[i] = s[i]; + return d + i; + } else { + return __repmovsb(d, s, n); + } + } +} + +static void *__asan_memcpy(void *dst, const void *src, size_t n) { + __asan_mempcpy(dst, src, n); + return dst; +} + static char *__asan_hexcpy(char *p, uint64_t x, uint8_t k) { while (k) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; return p; @@ -887,7 +960,7 @@ void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, __asan_poison(p + n, c - n, overrun); __asan_memset(p, 0xF9, n); __asan_write48(&e->size, n); - __builtin_memcpy(&e->bt, bt, sizeof(*bt)); + __asan_memcpy(&e->bt, bt, sizeof(*bt)); } return p; } @@ -1009,7 +1082,7 @@ static void *__asan_realloc_grow(void *p, size_t n, size_t m, struct AsanTrace *bt) { char *q; if ((q = __asan_allocate(16, n, kAsanHeapUnderrun, kAsanHeapOverrun, bt))) { - __builtin_memcpy(q, p, m); + __asan_memcpy(q, p, m); __asan_deallocate(p, kAsanHeapRelocated); } return q; diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 6978c7ce2..6bf9df84f 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -48,12 +48,9 @@ $(LIBC_INTRIN_A_OBJS): \ # we can't use asan and ubsan because: # this is asan and ubsan -# we need -ffreestanding because: -# we don't want __builtin_memcpy() calling memcpy() o/$(MODE)/libc/intrin/asan.o \ o/$(MODE)/libc/intrin/ubsan.o: \ OVERRIDE_CFLAGS += \ - -ffreestanding \ -fno-sanitize=all \ -fno-stack-protector From 99e67c348b9ac35e7ceecd4853da7371f5a275c7 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 23 May 2022 15:06:11 -0700 Subject: [PATCH 34/40] Reduce Makefile dependencies by 4% --- libc/calls/chdir-nt.c | 2 +- libc/calls/chdir.c | 3 +- libc/calls/chroot.c | 2 +- libc/calls/clock_gettime-nt.c | 1 - libc/calls/clock_gettime.c | 2 + libc/calls/close.c | 8 +- libc/calls/copyfile.c | 3 +- libc/calls/directmap-nt.c | 1 + libc/calls/directmap.c | 2 +- libc/calls/dup-nt.c | 2 + libc/calls/dup.c | 3 +- libc/calls/dup2.c | 3 +- libc/calls/dup3-sysv.c | 3 +- libc/calls/dup3.c | 3 +- libc/calls/execve-nt.greg.c | 1 + libc/calls/execve-sysv.c | 2 +- libc/calls/execve.c | 3 +- libc/calls/faccessat-nt.c | 2 +- libc/calls/faccessat.c | 3 +- libc/calls/fadvise-nt.c | 1 + libc/calls/fadvise.c | 3 +- libc/calls/fchdir-nt.c | 1 + libc/calls/fchdir.c | 3 +- libc/calls/fchmod.c | 2 +- libc/calls/fchmodat-nt.c | 2 +- libc/calls/fchmodat.c | 3 +- libc/calls/fchown.c | 2 +- libc/calls/fchownat.c | 2 +- libc/calls/fcntl-nt.c | 2 + libc/calls/fcntl-sysv.c | 3 +- libc/calls/fcntl.c | 2 + libc/calls/fdatasync.c | 3 +- libc/calls/fileexists.c | 3 +- libc/calls/fixenotdir.c | 2 +- libc/calls/fixupnewfd.c | 2 +- libc/calls/flock-nt.c | 1 + libc/calls/flock.c | 3 +- libc/calls/fstat-nt.c | 2 +- libc/calls/fstat-sysv.c | 3 + libc/calls/fstatat-nt.c | 2 + libc/calls/fstatat-sysv.c | 1 + libc/calls/fstatat.c | 1 + libc/calls/fsync.c | 3 +- libc/calls/ftruncate-nt.c | 2 +- libc/calls/ftruncate.c | 2 + libc/calls/g_sighandrvas.c | 1 - libc/calls/getcwd-nt.c | 2 +- libc/calls/getcwd-xnu.greg.c | 3 +- libc/calls/getcwd.greg.c | 4 +- libc/calls/getdomainname.c | 2 +- libc/calls/getegid.c | 2 +- libc/calls/geteuid.c | 2 +- libc/calls/getfiledescriptorsize.c | 1 + libc/calls/getfilesize.c | 1 - libc/calls/gethostname-linux.c | 1 - libc/calls/gethostname-nt.c | 3 +- libc/calls/gethostname.c | 2 +- libc/calls/getitimer.c | 1 + libc/calls/getpgid.c | 2 +- libc/calls/getpgrp.c | 2 +- libc/calls/getppid-nt.c | 1 - libc/calls/getppid.c | 3 +- libc/calls/getpriority-nt.c | 9 +- libc/calls/getpriority.c | 3 +- libc/calls/getprocaddressmodule.c | 1 - libc/calls/getprogramexecutablename.greg.c | 2 +- libc/calls/getresgid.c | 2 +- libc/calls/getresuid.c | 2 +- libc/calls/getrusage-nt.c | 3 +- libc/calls/getsid.c | 2 +- libc/calls/gettimeofday-nt.c | 1 - libc/calls/gettimeofday.c | 1 + libc/calls/getuid.c | 2 +- libc/calls/internal.h | 201 +----------------- libc/calls/interrupts-nt.c | 3 + libc/calls/ioctl_default.c | 2 + libc/calls/ioctl_fioclex.c | 1 + libc/calls/ioctl_siocgifconf-nt.c | 1 - libc/calls/ioctl_siocgifconf.c | 3 +- libc/calls/ioctl_tcgets.c | 2 + libc/calls/ioctl_tcsets.c | 1 + libc/calls/ioctl_tiocgwinsz-nt.c | 2 + libc/calls/ioctl_tiocgwinsz.c | 1 + libc/calls/ioctl_tiocswinsz-nt.c | 1 + libc/calls/ioctl_tiocswinsz.c | 1 + libc/calls/iovecsize.c | 1 + libc/calls/isatty.c | 2 + libc/calls/ischardev.c | 1 + libc/calls/isdirectory-nt.c | 2 +- libc/calls/isdirectory.c | 3 +- libc/calls/islinux.c | 1 - libc/calls/isregularfile-nt.c | 2 +- libc/calls/isregularfile.c | 3 +- libc/calls/issymlink-nt.c | 2 +- libc/calls/issymlink.c | 3 +- libc/calls/kill.c | 3 +- libc/calls/killpg.c | 2 +- libc/calls/lchown.c | 1 - libc/calls/link.c | 1 - libc/calls/linkat-nt.c | 2 +- libc/calls/linkat.c | 3 +- libc/calls/lseek-nt.c | 1 + libc/calls/lseek.c | 2 + libc/calls/madvise-nt.c | 2 +- libc/calls/madvise.c | 3 +- libc/calls/metaflock.c | 2 +- libc/calls/mincore.c | 2 +- libc/calls/mkdir.c | 2 +- libc/calls/mkdirat-nt.c | 2 +- libc/calls/mkdirat.c | 3 +- libc/calls/mkfifo.c | 2 +- libc/calls/mknod.c | 2 +- libc/calls/mkntpath.c | 2 +- libc/calls/mkntpathat.c | 1 + libc/calls/munmap-sysv.c | 2 +- libc/calls/nanosleep-nt.c | 1 + libc/calls/nanosleep.c | 1 + libc/calls/now.c | 2 + libc/calls/ntaccesscheck.c | 2 +- libc/calls/ntcontext2linux.c | 1 - libc/calls/ntreturn.c | 1 - libc/calls/ntsetprivilege.c | 1 - libc/calls/ntspawn.c | 2 +- libc/calls/offset2overlap.c | 2 +- libc/calls/oldbench.c | 2 +- libc/calls/open-nt.c | 37 ++-- libc/calls/openat-sysv.c | 2 +- libc/calls/openat.c | 3 + libc/calls/pause.c | 2 +- libc/calls/pipe-nt.c | 2 + libc/calls/pipe-sysv.c | 2 +- libc/calls/pipe.c | 3 +- libc/calls/pipe2-sysv.c | 3 +- libc/calls/pipe2.c | 3 +- libc/calls/poll-metal.c | 1 + libc/calls/poll-nt.c | 1 + libc/calls/poll.c | 1 - libc/calls/posix_openpt.c | 1 + libc/calls/pread.c | 1 + libc/calls/preadv.c | 2 + libc/calls/printfds.c | 1 + libc/calls/ptrace.c | 2 +- libc/calls/pwrite.c | 1 + libc/calls/pwritev.c | 1 + libc/calls/raise.c | 3 +- libc/calls/read-nt.c | 1 + libc/calls/read.c | 1 + libc/calls/readansi.c | 1 - libc/calls/readlinkat-nt.c | 2 +- libc/calls/readlinkat.c | 3 +- libc/calls/readv-nt.c | 1 + libc/calls/readv.c | 1 + libc/calls/reboot.c | 1 - libc/calls/renameat-nt.c | 2 +- libc/calls/renameat.c | 3 +- libc/calls/reservefd.c | 1 + libc/calls/rusage2linux.c | 1 + libc/calls/sched_getaffinity.c | 2 +- libc/calls/sched_setaffinity.c | 3 +- libc/calls/setgid.c | 2 +- libc/calls/setitimer-nt.c | 1 - libc/calls/setpgid.c | 3 +- libc/calls/setpriority-nt.c | 2 +- libc/calls/setpriority.c | 3 +- libc/calls/setregid.c | 2 +- libc/calls/setresgid.c | 2 +- libc/calls/setresuid.c | 2 +- libc/calls/setreuid.c | 2 +- libc/calls/setsid.c | 2 +- libc/calls/setuid.c | 2 +- libc/calls/sig.c | 2 +- libc/calls/sig2.c | 2 +- libc/calls/sigaction.c | 3 + libc/calls/sigaltstack.c | 2 +- libc/calls/sigchld-nt.c | 2 + libc/calls/sigenter-freebsd.c | 1 + libc/calls/sigenter-netbsd.c | 1 + libc/calls/sigenter-openbsd.c | 1 + libc/calls/sigenter-xnu.c | 1 + libc/calls/sigprocmask-sysv.greg.c | 1 + libc/calls/sleep.c | 1 - libc/calls/splice.c | 1 + libc/calls/stat2cosmo.c | 1 - libc/calls/state.internal.h | 16 ++ libc/calls/symlinkat-nt.c | 2 +- libc/calls/symlinkat.c | 3 +- libc/calls/sync-nt.c | 2 + libc/calls/sync.c | 3 +- libc/calls/sync_file_range.c | 2 +- libc/calls/sys_utimes_nt.c | 1 + libc/calls/syscall-nt.internal.h | 43 ++++ libc/calls/syscall-sysv.internal.h | 123 +++++++++++ libc/calls/syscall_support-nt.internal.h | 27 +++ libc/calls/syscall_support-sysv.internal.h | 25 +++ libc/calls/sysinfo-nt.c | 2 +- libc/calls/sysinfo.c | 7 +- libc/calls/tcdrain.c | 1 - libc/calls/tcflow.c | 3 +- libc/calls/tcflush.c | 2 +- libc/calls/tcgetsid.c | 2 +- libc/calls/tcsendbreak.c | 2 +- libc/calls/tgkill.c | 2 +- libc/calls/timecritical.c | 1 - libc/calls/tkill.c | 3 +- libc/calls/truncate-nt.c | 3 +- libc/calls/truncate.c | 4 +- libc/calls/ttyname_r.c | 1 + libc/calls/umask.c | 2 +- libc/calls/uname.c | 3 +- libc/calls/unlinkat-nt.c | 3 +- libc/calls/unlinkat.c | 4 +- libc/calls/usleep.c | 1 - libc/calls/utimensat-nt.c | 2 +- libc/calls/vdprintf.c | 1 + libc/calls/vdsofunc.greg.c | 1 - libc/calls/wait3.c | 1 - libc/calls/wait4-nt.c | 2 + libc/calls/wincrash.c | 1 + libc/calls/winerr.internal.h | 8 + libc/calls/write-nt.c | 1 + libc/calls/write.c | 1 + libc/calls/writev-metal.c | 1 + libc/calls/writev-serial.c | 1 + libc/calls/writev.c | 1 + libc/dns/getntnameservers.c | 2 +- libc/intrin/asan.c | 2 +- libc/intrin/closehandle.c | 2 +- libc/intrin/createdirectory.c | 2 +- libc/intrin/createfile.c | 2 +- libc/intrin/createfilemapping.c | 2 +- libc/intrin/createfilemappingnuma.c | 2 +- libc/intrin/createnamedpipe.c | 2 +- libc/intrin/createpipe.c | 2 +- libc/intrin/createprocess.c | 2 +- libc/intrin/createsymboliclink.c | 2 +- libc/intrin/createthread.c | 2 +- libc/intrin/deletefile.c | 2 +- .../describentsecurityattributes.greg.c | 2 +- libc/intrin/deviceiocontrol.greg.c | 2 +- libc/intrin/findclose.c | 2 +- libc/intrin/findfirstfile.c | 2 +- libc/intrin/findnextfile.c | 2 +- libc/intrin/flushfilebuffers.c | 2 +- libc/intrin/flushviewoffile.c | 2 +- libc/intrin/generateconsolectrlevent.c | 2 +- libc/intrin/getexitcodeprocess.c | 2 +- libc/intrin/getfileattributes.c | 2 +- libc/intrin/getpid.c | 3 +- libc/intrin/kprintf.greg.c | 2 +- libc/intrin/mapviewoffileex.c | 2 +- libc/intrin/mapviewoffileexnuma.c | 2 +- libc/intrin/movefileex.c | 2 +- libc/intrin/openprocess.c | 2 +- libc/intrin/prot2nt.greg.c | 1 - libc/intrin/releasefd.c | 1 + libc/intrin/removedirectory.c | 2 +- libc/intrin/reopenfile.c | 2 +- libc/intrin/setcurrentdirectory.c | 2 +- libc/intrin/terminateprocess.c | 2 +- libc/intrin/unmapviewoffile.c | 2 +- libc/intrin/vforked.c | 1 - libc/intrin/virtualprotect.c | 2 +- libc/intrin/waitformultipleobjects.c | 2 +- libc/intrin/waitforsingleobject.c | 2 +- libc/log/backtrace2.greg.c | 2 +- libc/log/libfatal.internal.h | 1 - libc/log/oncrash.c | 2 +- libc/log/restoretty.greg.c | 2 +- libc/mem/pledge.c | 31 +-- libc/rand/getrandom.c | 3 +- libc/runtime/arch_prctl.c | 2 +- libc/runtime/clone.c | 1 - libc/runtime/fork-nt.c | 2 + libc/runtime/fork.c | 4 +- libc/runtime/getinterpreterexecutablename.c | 2 +- libc/runtime/getmaxfd.c | 2 +- libc/runtime/hook.greg.c | 1 - libc/runtime/mmap.c | 1 + libc/runtime/mprotect.greg.c | 2 +- libc/runtime/mremap.c | 1 - libc/runtime/msync-nt.c | 1 - libc/runtime/msync.c | 3 +- libc/runtime/munmap.c | 3 +- libc/runtime/stackuse.c | 1 - libc/runtime/winmain.greg.c | 3 +- libc/sock/accept-nt.c | 9 +- libc/sock/accept4.c | 2 +- libc/sock/bind-nt.c | 5 +- libc/sock/bind.c | 1 + libc/sock/closesocket-nt.c | 1 + libc/sock/connect-nt.c | 2 +- libc/sock/connect.c | 1 + libc/sock/epoll.c | 2 + libc/sock/firewall.c | 1 + libc/sock/fixupnewsockfd.c | 2 +- libc/sock/gethostips.c | 6 +- libc/sock/getpeername-nt.c | 2 +- libc/sock/getpeername.c | 2 +- libc/sock/getsockname-nt.c | 4 +- libc/sock/getsockname.c | 2 +- libc/sock/getsockopt-nt.c | 5 +- libc/sock/getsockopt.c | 2 +- libc/sock/inet_pton.c | 1 + libc/sock/internal.h | 14 -- libc/sock/listen-nt.c | 3 +- libc/sock/listen.c | 1 + libc/sock/recv-nt.c | 2 +- libc/sock/recv.c | 1 + libc/sock/recvfrom-nt.c | 2 +- libc/sock/recvfrom.c | 2 +- libc/sock/recvmsg.c | 1 + libc/sock/select-nt.c | 1 + libc/sock/sendfile.c | 2 + libc/sock/sendfile.internal.h | 16 ++ libc/sock/setsockopt-nt.c | 1 + libc/sock/setsockopt.c | 1 + libc/sock/shutdown-nt.c | 2 +- libc/sock/shutdown.c | 1 + libc/sock/socket-nt.c | 1 + libc/sock/socketpair-nt.c | 3 + libc/sock/sys_sendfile_freebsd.S | 23 ++ libc/sock/sys_sendfile_xnu.S | 23 ++ libc/sock/syscall_fd.internal.h | 24 +++ libc/sock/wsablock.c | 1 + libc/stdio/dirstream.c | 1 + libc/testlib/testmain.c | 2 +- libc/testlib/testrunner.c | 2 +- libc/thread/exit.c | 2 +- libc/time/times.c | 2 +- libc/zipos/close.c | 2 + libc/zipos/open.c | 1 + libc/zipos/read.c | 1 - test/libc/calls/mkntpath_test.c | 1 + test/libc/calls/read_test.c | 1 + test/libc/calls/seccomp_test.c | 1 + test/libc/calls/vfork_test.c | 1 + test/libc/calls/write_test.c | 1 + test/libc/mem/pledge_test.c | 1 + third_party/python/Modules/posixmodule.c | 2 + 339 files changed, 756 insertions(+), 499 deletions(-) create mode 100644 libc/calls/state.internal.h create mode 100644 libc/calls/syscall-nt.internal.h create mode 100644 libc/calls/syscall-sysv.internal.h create mode 100644 libc/calls/syscall_support-nt.internal.h create mode 100644 libc/calls/syscall_support-sysv.internal.h create mode 100644 libc/calls/winerr.internal.h create mode 100644 libc/sock/sendfile.internal.h create mode 100644 libc/sock/sys_sendfile_freebsd.S create mode 100644 libc/sock/sys_sendfile_xnu.S create mode 100644 libc/sock/syscall_fd.internal.h diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index 65c027ce5..75a3e0ad0 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/macros.internal.h" #include "libc/nt/errors.h" diff --git a/libc/calls/chdir.c b/libc/calls/chdir.c index 0b42d5a95..8afec0deb 100644 --- a/libc/calls/chdir.c +++ b/libc/calls/chdir.c @@ -16,8 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/chroot.c b/libc/calls/chroot.c index 2f654f7a4..0ea2f439e 100644 --- a/libc/calls/chroot.c +++ b/libc/calls/chroot.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Changes root directory. diff --git a/libc/calls/clock_gettime-nt.c b/libc/calls/clock_gettime-nt.c index 2d7ecef12..e9bca40f2 100644 --- a/libc/calls/clock_gettime-nt.c +++ b/libc/calls/clock_gettime-nt.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/fmt/conv.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nt/synchronization.h" diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index f7deab6e6..ab086c746 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -18,8 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/close.c b/libc/calls/close.c index ef1a248fe..1a241f007 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -17,13 +17,13 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" -#include "libc/intrin/kprintf.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/intrin/spinlock.h" -#include "libc/macros.internal.h" -#include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" diff --git a/libc/calls/copyfile.c b/libc/calls/copyfile.c index 270532537..31cd2422f 100644 --- a/libc/calls/copyfile.c +++ b/libc/calls/copyfile.c @@ -16,9 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/copyfile.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/nt/createfile.h" diff --git a/libc/calls/directmap-nt.c b/libc/calls/directmap-nt.c index e8f015ede..fb4921776 100644 --- a/libc/calls/directmap-nt.c +++ b/libc/calls/directmap-nt.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/kprintf.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" diff --git a/libc/calls/directmap.c b/libc/calls/directmap.c index 7c8a12d6d..502f0fbd4 100644 --- a/libc/calls/directmap.c +++ b/libc/calls/directmap.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/runtime.h" diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index e819fb3a6..8e5894427 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -20,6 +20,8 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/files.h" diff --git a/libc/calls/dup.c b/libc/calls/dup.c index 84ce876ee..4bcea1e12 100644 --- a/libc/calls/dup.c +++ b/libc/calls/dup.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/dup2.c b/libc/calls/dup2.c index ab8b79449..bb78e74dd 100644 --- a/libc/calls/dup2.c +++ b/libc/calls/dup2.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/dup3-sysv.c b/libc/calls/dup3-sysv.c index ec9f64650..2bbea134c 100644 --- a/libc/calls/dup3-sysv.c +++ b/libc/calls/dup3-sysv.c @@ -16,8 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/errno.h" int32_t sys_dup3(int32_t oldfd, int32_t newfd, int flags) { diff --git a/libc/calls/dup3.c b/libc/calls/dup3.c index f19dccab9..05128a0a5 100644 --- a/libc/calls/dup3.c +++ b/libc/calls/dup3.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/execve-nt.greg.c b/libc/calls/execve-nt.greg.c index 003a761dd..3418744c3 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/calls/execve-nt.greg.c @@ -22,6 +22,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ntspawn.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" #include "libc/mem/alloca.h" #include "libc/nt/accounting.h" #include "libc/nt/console.h" diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 60c4f5189..7c39f32c0 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/errno.h" #include "libc/mem/alloca.h" #include "libc/paths.h" diff --git a/libc/calls/execve.c b/libc/calls/execve.c index ab4ac6ac7..df59f26fa 100644 --- a/libc/calls/execve.c +++ b/libc/calls/execve.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index 2630da0fb..96b717084 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 2e602e121..e682d4f83 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/fadvise-nt.c b/libc/calls/fadvise-nt.c index 82d818be8..9c8cae641 100644 --- a/libc/calls/fadvise-nt.c +++ b/libc/calls/fadvise-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filesharemode.h" diff --git a/libc/calls/fadvise.c b/libc/calls/fadvise.c index 31044b604..31425e4f7 100644 --- a/libc/calls/fadvise.c +++ b/libc/calls/fadvise.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/fchdir-nt.c b/libc/calls/fchdir-nt.c index ab8db742c..f01d804b3 100644 --- a/libc/calls/fchdir-nt.c +++ b/libc/calls/fchdir-nt.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/nt/files.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/fchdir.c b/libc/calls/fchdir.c index 614a7fb57..83f7ad7d2 100644 --- a/libc/calls/fchdir.c +++ b/libc/calls/fchdir.c @@ -17,7 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/fchmod.c b/libc/calls/fchmod.c index 39baf82e0..27996c8fb 100644 --- a/libc/calls/fchmod.c +++ b/libc/calls/fchmod.c @@ -17,7 +17,7 @@ │ 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/sysv/errfuns.h" diff --git a/libc/calls/fchmodat-nt.c b/libc/calls/fchmodat-nt.c index 7b0ba5f7c..ee9966a9f 100644 --- a/libc/calls/fchmodat-nt.c +++ b/libc/calls/fchmodat-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/files.h" diff --git a/libc/calls/fchmodat.c b/libc/calls/fchmodat.c index f8b617442..5ea267d00 100644 --- a/libc/calls/fchmodat.c +++ b/libc/calls/fchmodat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/fchown.c b/libc/calls/fchown.c index 6f7f5f1c5..f447de5fe 100644 --- a/libc/calls/fchown.c +++ b/libc/calls/fchown.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/sysv/consts/at.h" /** diff --git a/libc/calls/fchownat.c b/libc/calls/fchownat.c index cec5ab92b..bf743aa5c 100644 --- a/libc/calls/fchownat.c +++ b/libc/calls/fchownat.c @@ -18,8 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index d51cdc0a9..3eaf7fb6a 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -19,6 +19,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/flock.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" diff --git a/libc/calls/fcntl-sysv.c b/libc/calls/fcntl-sysv.c index 0e9a04ff2..9e9bca098 100644 --- a/libc/calls/fcntl-sysv.c +++ b/libc/calls/fcntl-sysv.c @@ -16,8 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/flock.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/sysv/consts/f.h" int sys_fcntl(int fd, int cmd, uintptr_t arg) { diff --git a/libc/calls/fcntl.c b/libc/calls/fcntl.c index 44d469619..b87653c85 100644 --- a/libc/calls/fcntl.c +++ b/libc/calls/fcntl.c @@ -20,6 +20,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" diff --git a/libc/calls/fdatasync.c b/libc/calls/fdatasync.c index 6e72c1c2c..c2c12dd81 100644 --- a/libc/calls/fdatasync.c +++ b/libc/calls/fdatasync.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/fileexists.c b/libc/calls/fileexists.c index 876a1cac9..f5301a8fb 100644 --- a/libc/calls/fileexists.c +++ b/libc/calls/fileexists.c @@ -18,10 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.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/asan.internal.h" diff --git a/libc/calls/fixenotdir.c b/libc/calls/fixenotdir.c index 8b982fca4..5b9146b38 100644 --- a/libc/calls/fixenotdir.c +++ b/libc/calls/fixenotdir.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/errno.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" +#include "libc/str/str.h" static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) { int e; diff --git a/libc/calls/fixupnewfd.c b/libc/calls/fixupnewfd.c index c7a65c920..93911893a 100644 --- a/libc/calls/fixupnewfd.c +++ b/libc/calls/fixupnewfd.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/o.h" diff --git a/libc/calls/flock-nt.c b/libc/calls/flock-nt.c index 85a9a879d..764905d20 100644 --- a/libc/calls/flock-nt.c +++ b/libc/calls/flock-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/byhandlefileinformation.h" diff --git a/libc/calls/flock.c b/libc/calls/flock.c index b48232f7e..2f0afbdb3 100644 --- a/libc/calls/flock.c +++ b/libc/calls/flock.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 125ef8e09..c869fb6d9 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/conv.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" diff --git a/libc/calls/fstat-sysv.c b/libc/calls/fstat-sysv.c index e05714552..22c4cd7f7 100644 --- a/libc/calls/fstat-sysv.c +++ b/libc/calls/fstat-sysv.c @@ -17,6 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/metastat.internal.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 468752e70..853744b91 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -17,6 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" diff --git a/libc/calls/fstatat-sysv.c b/libc/calls/fstatat-sysv.c index 4c64f3986..45a9f78f3 100644 --- a/libc/calls/fstatat-sysv.c +++ b/libc/calls/fstatat-sysv.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/struct/metastat.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/fstatat.c b/libc/calls/fstatat.c index 0a13947a1..b59ca4639 100644 --- a/libc/calls/fstatat.c +++ b/libc/calls/fstatat.c @@ -19,6 +19,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" diff --git a/libc/calls/fsync.c b/libc/calls/fsync.c index e3b251fbf..859c858f6 100644 --- a/libc/calls/fsync.c +++ b/libc/calls/fsync.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/ftruncate-nt.c b/libc/calls/ftruncate-nt.c index 9c1e79288..46574efa2 100644 --- a/libc/calls/ftruncate-nt.c +++ b/libc/calls/ftruncate-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/enum/filemovemethod.h" #include "libc/nt/files.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/ftruncate.c b/libc/calls/ftruncate.c index 5c17d5780..109c7b2f6 100644 --- a/libc/calls/ftruncate.c +++ b/libc/calls/ftruncate.c @@ -19,6 +19,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/g_sighandrvas.c b/libc/calls/g_sighandrvas.c index e7b259cb7..e1da914a5 100644 --- a/libc/calls/g_sighandrvas.c +++ b/libc/calls/g_sighandrvas.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" _Alignas(64) int __sig_lock; unsigned __sighandrvas[NSIG]; diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index 475824679..ed6c962d6 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" #include "libc/str/str.h" diff --git a/libc/calls/getcwd-xnu.greg.c b/libc/calls/getcwd-xnu.greg.c index 63bfac438..7d3cb5b19 100644 --- a/libc/calls/getcwd-xnu.greg.c +++ b/libc/calls/getcwd-xnu.greg.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" diff --git a/libc/calls/getcwd.greg.c b/libc/calls/getcwd.greg.c index 158185a56..52dacfbe3 100644 --- a/libc/calls/getcwd.greg.c +++ b/libc/calls/getcwd.greg.c @@ -19,8 +19,10 @@ #include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/log/backtrace.internal.h" #include "libc/mem/mem.h" diff --git a/libc/calls/getdomainname.c b/libc/calls/getdomainname.c index 87dd1d748..691d08b7d 100644 --- a/libc/calls/getdomainname.c +++ b/libc/calls/getdomainname.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/utsname.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/nt/enum/computernameformat.h" diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index e142d34f1..e4516baca 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index ad16d7419..4db32a100 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Returns effective user ID of calling process. diff --git a/libc/calls/getfiledescriptorsize.c b/libc/calls/getfiledescriptorsize.c index 045e3ecd0..8011608cd 100644 --- a/libc/calls/getfiledescriptorsize.c +++ b/libc/calls/getfiledescriptorsize.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/nt/enum/fileinfobyhandleclass.h" diff --git a/libc/calls/getfilesize.c b/libc/calls/getfilesize.c index 8ef96630c..69da98ad1 100644 --- a/libc/calls/getfilesize.c +++ b/libc/calls/getfilesize.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/limits.h" diff --git a/libc/calls/gethostname-linux.c b/libc/calls/gethostname-linux.c index fac5b9b9c..979cfb6e2 100644 --- a/libc/calls/gethostname-linux.c +++ b/libc/calls/gethostname-linux.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/utsname.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/gethostname-nt.c b/libc/calls/gethostname-nt.c index 2cdf8c0f6..906d29980 100644 --- a/libc/calls/gethostname-nt.c +++ b/libc/calls/gethostname-nt.c @@ -17,7 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/enum/computernameformat.h" #include "libc/nt/systeminfo.h" #include "libc/str/str.h" diff --git a/libc/calls/gethostname.c b/libc/calls/gethostname.c index eaafb2b43..743525483 100644 --- a/libc/calls/gethostname.c +++ b/libc/calls/gethostname.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/nt/enum/computernameformat.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/getitimer.c b/libc/calls/getitimer.c index 57b8937a9..0467eb522 100644 --- a/libc/calls/getitimer.c +++ b/libc/calls/getitimer.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/struct/itimerval.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/getpgid.c b/libc/calls/getpgid.c index 8ae485a7e..a072d98c1 100644 --- a/libc/calls/getpgid.c +++ b/libc/calls/getpgid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/getpgrp.c b/libc/calls/getpgrp.c index 9a15d1c6e..d083b42c3 100644 --- a/libc/calls/getpgrp.c +++ b/libc/calls/getpgrp.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/getppid-nt.c b/libc/calls/getppid-nt.c index b6f7c0d5a..0410b704a 100644 --- a/libc/calls/getppid-nt.c +++ b/libc/calls/getppid-nt.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/nt/nt/process.h" #include "libc/nt/ntdll.h" diff --git a/libc/calls/getppid.c b/libc/calls/getppid.c index d997f6134..2d6c24133 100644 --- a/libc/calls/getppid.c +++ b/libc/calls/getppid.c @@ -16,8 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Returns parent process id. diff --git a/libc/calls/getpriority-nt.c b/libc/calls/getpriority-nt.c index 3f89a9401..5bd4a2b98 100644 --- a/libc/calls/getpriority-nt.c +++ b/libc/calls/getpriority-nt.c @@ -16,20 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/kntprioritycombos.internal.h" -#include "libc/fmt/conv.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nexgen32e/ffs.h" -#include "libc/nt/enum/processcreationflags.h" -#include "libc/nt/enum/threadpriority.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" #include "libc/runtime/runtime.h" -#include "libc/sysv/consts/prio.h" -#include "libc/sysv/errfuns.h" textwindows int sys_getpriority_nt(int ignored) { size_t i; diff --git a/libc/calls/getpriority.c b/libc/calls/getpriority.c index 6a65f48d1..fb7a61967 100644 --- a/libc/calls/getpriority.c +++ b/libc/calls/getpriority.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Returns nice value of thing. diff --git a/libc/calls/getprocaddressmodule.c b/libc/calls/getprocaddressmodule.c index cba122484..f45c39471 100644 --- a/libc/calls/getprocaddressmodule.c +++ b/libc/calls/getprocaddressmodule.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/nt/dll.h" /** diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index b6ab8a1e6..3c34e73c1 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -17,7 +17,7 @@ │ 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/macros.internal.h" diff --git a/libc/calls/getresgid.c b/libc/calls/getresgid.c index 1760de051..36aa25dd8 100644 --- a/libc/calls/getresgid.c +++ b/libc/calls/getresgid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/getresuid.c b/libc/calls/getresuid.c index 80a0f10a7..d67c4c28a 100644 --- a/libc/calls/getresuid.c +++ b/libc/calls/getresuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 35bb44e50..8c4fa79c8 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -17,9 +17,10 @@ │ 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/rusage.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/conv.h" #include "libc/intrin/spinlock.h" #include "libc/nt/accounting.h" diff --git a/libc/calls/getsid.c b/libc/calls/getsid.c index 1d199e2b0..057aeb54e 100644 --- a/libc/calls/getsid.c +++ b/libc/calls/getsid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Creates session and sets the process group id. diff --git a/libc/calls/gettimeofday-nt.c b/libc/calls/gettimeofday-nt.c index cf113f721..8b808db05 100644 --- a/libc/calls/gettimeofday-nt.c +++ b/libc/calls/gettimeofday-nt.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" #include "libc/fmt/conv.h" #include "libc/nt/struct/filetime.h" diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 927e785a4..5698a2370 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index 4c0d49be4..4136a304e 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/nt/accounting.h" diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 44e6fa79a..482ff1476 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -1,7 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/metastat.internal.h" @@ -72,16 +71,8 @@ struct Fds { struct Fd __init_p[OPEN_MAX]; }; -extern const struct Fd kEmptyFd; - -hidden extern int __vforked; -hidden extern int __fds_lock; -hidden extern int __sig_lock; -hidden extern bool __time_critical; -hidden extern unsigned __sighandrvas[NSIG]; -hidden extern unsigned __sighandflags[NSIG]; hidden extern struct Fds g_fds; -hidden extern const struct NtSecurityAttributes kNtIsInheritable; +hidden extern const struct Fd kEmptyFd; int __reservefd(int) hidden; int __reservefd_unlocked(int) hidden; @@ -121,254 +112,78 @@ forceinline size_t _clampio(size_t size) { │ cosmopolitan § syscalls » system five » synthetic jump slots ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -axdx_t __sys_pipe(i32[hasatleast 2], i32) hidden; -axdx_t sys_fork(void) hidden; -axdx_t sys_getpid(void) hidden; axdx_t sys_gettimeofday(struct timeval *, struct timezone *, void *) hidden; -char *sys_getcwd(char *, u64) hidden; -char *sys_getcwd_xnu(char *, u64) hidden; -i32 __sys_dup3(i32, i32, i32) hidden; -i32 __sys_execve(const char *, char *const[], char *const[]) hidden; -i32 __sys_fcntl(i32, i32, ...) hidden; -i32 __sys_fstat(i32, void *) hidden; -i32 __sys_fstatat(i32, const char *, void *, i32) hidden; i32 __sys_getrusage(i32, struct rusage *) hidden; -i32 __sys_munmap(void *, u64) hidden; -i32 __sys_openat(i32, const char *, i32, u32) hidden; -i32 __sys_pipe2(i32[hasatleast 2], u32) hidden; i32 __sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; i32 __sys_wait4(i32, i32 *, i32, struct rusage *) hidden; -i32 sys_arch_prctl(i32, i64) hidden; -i32 sys_chdir(const char *) hidden; -i32 sys_chroot(const char *) hidden; i32 sys_clock_gettime(i32, struct timespec *) hidden; -i32 sys_close(i32) hidden; -i32 sys_dup(i32) hidden; -i32 sys_dup2(i32, i32) hidden; -i32 sys_dup3(i32, i32, i32) hidden; -i32 sys_execve(const char *, char *const[], char *const[]) hidden; -i32 sys_faccessat(i32, const char *, i32, u32) hidden; -i32 sys_fadvise(i32, i64, i64, i32) hidden; -i32 sys_fchdir(i32) hidden; -i32 sys_fchmod(i32, u32) hidden; -i32 sys_fchmodat(i32, const char *, u32, u32) hidden; -i32 sys_fchown(i64, u32, u32) hidden; -i32 sys_fchownat(i32, const char *, u32, u32, u32) hidden; -i32 sys_fcntl(i32, i32, u64) hidden; -i32 sys_fdatasync(i32) hidden; -i32 sys_flock(i32, i32) hidden; i32 sys_fstat(i32, struct stat *) hidden; i32 sys_fstatat(i32, const char *, struct stat *, i32) hidden; -i32 sys_fsync(i32) hidden; -i32 sys_ftruncate(i32, i64, i64) hidden; i32 sys_futimes(i32, const struct timeval *) hidden; i32 sys_futimesat(i32, const char *, const struct timeval *) hidden; -i32 sys_getcontext(void *) hidden; i32 sys_getitimer(i32, struct itimerval *) hidden; -i32 sys_getpgid(i32) hidden; -i32 sys_getpgrp(void) hidden; -i32 sys_getppid(void) hidden; -i32 sys_getpriority(i32, u32) hidden; -i32 sys_getresgid(u32 *, u32 *, u32 *); -i32 sys_getresuid(u32 *, u32 *, u32 *); i32 sys_getrlimit(i32, struct rlimit *) hidden; i32 sys_getrusage(i32, struct rusage *) hidden; -i32 sys_getsid(int) hidden; -i32 sys_ioctl(i32, u64, ...) hidden; -i32 sys_kill(i32, i32, i32) hidden; -i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden; -i32 sys_lseek(i32, i64, i64, i64) hidden; i32 sys_lutimes(const char *, const struct timeval *) hidden; -i32 sys_madvise(void *, size_t, i32) hidden; -i32 sys_memfd_create(const char *, u32) hidden; -i32 sys_mincore(void *, u64, unsigned char *) hidden; -i32 sys_mkdirat(i32, const char *, u32) hidden; -i32 sys_mkfifo(const char *, u32) hidden; -i32 sys_mknod(const char *, u32, u64) hidden; -i32 sys_mprotect(void *, u64, i32) hidden; -i32 sys_msync(void *, u64, i32) hidden; -i32 sys_munmap(void *, u64) hidden; i32 sys_nanosleep(const struct timespec *, struct timespec *) hidden; -i32 sys_openat(i32, const char *, i32, u32) hidden; -i32 sys_pause(void) hidden; -i32 sys_pipe(i32[hasatleast 2]) hidden; -i32 sys_pipe2(i32[hasatleast 2], u32) hidden; -i32 sys_pledge(const char *, const char *) hidden; -i32 sys_posix_openpt(i32) hidden; -i32 sys_renameat(i32, const char *, i32, const char *) hidden; -i32 sys_sched_setaffinity(i32, u64, const void *) hidden; -i32 sys_sched_yield(void) hidden; -i32 sys_setgid(i32) hidden; i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden; -i32 sys_setpgid(i32, i32) hidden; -i32 sys_setpriority(i32, u32, i32) hidden; -i32 sys_setregid(u32, u32) hidden; -i32 sys_setresgid(u32, u32, u32) hidden; -i32 sys_setresuid(u32, u32, u32) hidden; -i32 sys_setreuid(u32, u32) hidden; i32 sys_setrlimit(i32, const struct rlimit *) hidden; -i32 sys_setsid(void) hidden; -i32 sys_setuid(i32) hidden; -i32 sys_sigaction(i32, const void *, void *, i64, i64) hidden; -i32 sys_sigaltstack(const void *, void *) hidden; i32 sys_sigprocmask(i32, const sigset *, sigset *) hidden; i32 sys_sigqueue(i32, i32, const union sigval) hidden; i32 sys_sigqueueinfo(i32, const siginfo_t *) hidden; i32 sys_sigsuspend(const sigset *, u64) hidden; -i32 sys_symlinkat(const char *, i32, const char *) hidden; -i32 sys_sync(void) hidden; -i32 sys_sync_file_range(i32, i64, i64, u32) hidden; i32 sys_sysinfo(struct sysinfo *) hidden; -i32 sys_tgkill(i32, i32, i32) hidden; -i32 sys_tkill(i32, i32, void *) hidden; -i32 sys_truncate(const char *, u64, u64) hidden; -i32 sys_uname(char *) hidden; -i32 sys_unlinkat(i32, const char *, i32) hidden; i32 sys_utime(const char *, const struct utimbuf *) hidden; i32 sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; i32 sys_utimes(const char *, const struct timeval *) hidden; i32 sys_wait4(i32, i32 *, i32, struct rusage *) hidden; -i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden; -i64 sys_getrandom(void *, u64, u32) hidden; -i64 sys_pread(i32, void *, u64, i64, i64) hidden; i64 sys_preadv(i32, struct iovec *, i32, i64, i64) hidden; -i64 sys_ptrace(int, i32, void *, void *) hidden; -i64 sys_pwrite(i32, const void *, u64, i64, i64) hidden; i64 sys_pwritev(i32, const struct iovec *, i32, i64, i64) hidden; -i64 sys_read(i32, void *, u64) hidden; -i64 sys_readlink(const char *, char *, u64) hidden; -i64 sys_readlinkat(int, const char *, char *, u64) hidden; -i64 sys_sched_getaffinity(i32, u64, void *) hidden; -i64 sys_sendfile(i32, i32, i64 *, u64) hidden; -i64 sys_splice(i32, i64 *, i32, i64 *, u64, u32) hidden; i64 sys_vmsplice(i32, const struct iovec *, i64, u32) hidden; -i64 sys_write(i32, const void *, u64) hidden; -u32 sys_getegid(void) hidden; -u32 sys_geteuid(void) hidden; -u32 sys_getgid(void) hidden; -u32 sys_gettid(void) hidden; -u32 sys_getuid(void) hidden; -u32 sys_umask(u32) hidden; -void *__sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden; -void *sys_mremap(void *, u64, u64, i32, void *) hidden; -void sys_exit(int) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » system five » support ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -void __onfork(void) hidden; -void *__vdsofunc(const char *) hidden; -void *__get_clock_gettime(void) hidden; -i32 __fixupnewfd(i32, i32) hidden; -void __restore_rt() hidden; -int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden; int sys_nanosleep_xnu(const struct timespec *, struct timespec *) hidden; -void __stat2cosmo(struct stat *restrict, const union metastat *) hidden; -void __restore_rt_netbsd(void) hidden; +int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden; +size_t __iovec_size(const struct iovec *, size_t) hidden; +ssize_t WritevUninterruptible(int, struct iovec *, int); +void __rusage2linux(struct rusage *) hidden; void __sigenter_xnu(void *, i32, i32, struct siginfo_xnu *, struct __darwin_ucontext *) hidden; -int gethostname_linux(char *, size_t) hidden; -int gethostname_bsd(char *, size_t) hidden; -int gethostname_nt(char *, size_t, int) hidden; -size_t __iovec_size(const struct iovec *, size_t) hidden; -void __rusage2linux(struct rusage *) hidden; -int __notziposat(int, const char *); -ssize_t WritevUninterruptible(int, struct iovec *, int); -void flock2cosmo(uintptr_t); -void cosmo2flock(uintptr_t); - -int sys_sendfile_xnu(int32_t infd, int32_t outfd, int64_t offset, - int64_t *out_opt_sbytes, const void *opt_hdtr, - int32_t flags) asm("sys_sendfile") hidden; -int sys_sendfile_freebsd(int32_t infd, int32_t outfd, int64_t offset, - size_t nbytes, const void *opt_hdtr, - int64_t *out_opt_sbytes, - int32_t flags) asm("sys_sendfile") hidden; +void __stat2cosmo(struct stat *restrict, const union metastat *) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » windows nt » veneers ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -bool32 sys_isatty_nt(int) hidden; -char *sys_getcwd_nt(char *, size_t) hidden; -i64 sys_lseek_nt(int, i64, int) hidden; -int sys_chdir_nt(const char *) hidden; -int sys_close_epoll_nt(int) hidden; +int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden; int sys_close_nt(struct Fd *) hidden; -int sys_dup_nt(int, int, int, int) hidden; -int sys_execve_nt(const char *, char *const[], char *const[]) hidden; -int sys_faccessat_nt(int, const char *, int, uint32_t) hidden; -int sys_fadvise_nt(int, u64, u64, int) hidden; -int sys_fchdir_nt(int) hidden; -int sys_fchmodat_nt(int, const char *, uint32_t, int) hidden; -int sys_fcntl_nt(int, int, uintptr_t) hidden; -int sys_fdatasync_nt(int) hidden; -int sys_flock_nt(int, int) hidden; -int sys_fork_nt(void) hidden; int sys_fstat_nt(i64, struct stat *) hidden; int sys_fstatat_nt(int, const char *, struct stat *, int) hidden; -int sys_ftruncate_nt(i64, u64) hidden; -int sys_getppid_nt(void) hidden; -int sys_getpriority_nt(int) hidden; int sys_getrusage_nt(int, struct rusage *) hidden; int sys_gettimeofday_nt(struct timeval *, struct timezone *) hidden; -int sys_kill_nt(int, int) hidden; -int sys_linkat_nt(int, const char *, int, const char *) hidden; int sys_lstat_nt(const char *, struct stat *) hidden; -int sys_madvise_nt(void *, size_t, int) hidden; -int sys_mkdirat_nt(int, const char *, uint32_t) hidden; -int sys_msync_nt(char *, size_t, int) hidden; int sys_nanosleep_nt(const struct timespec *, struct timespec *) hidden; -int sys_pipe_nt(int[hasatleast 2], unsigned) hidden; -int sys_renameat_nt(int, const char *, int, const char *) hidden; -int sys_sched_yield_nt(void) hidden; int sys_setitimer_nt(int, const struct itimerval *, struct itimerval *) hidden; -int sys_setpriority_nt(int) hidden; -int sys_symlinkat_nt(const char *, int, const char *) hidden; -int sys_sync_nt(void) hidden; -int sys_sysinfo_nt(struct sysinfo *) hidden; -int sys_truncate_nt(const char *, u64) hidden; -int sys_unlinkat_nt(int, const char *, int) hidden; int sys_setrlimit_nt(int, const struct rlimit *) hidden; +int sys_sysinfo_nt(struct sysinfo *) hidden; int sys_utimensat_nt(int, const char *, const struct timespec *, int) hidden; int sys_utimes_nt(const char *, const struct timeval[2]) hidden; -ssize_t sys_open_nt(int, const char *, u32, i32) dontdiscard hidden; ssize_t sys_read_nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; -ssize_t sys_readlinkat_nt(int, const char *, char *, size_t) hidden; ssize_t sys_write_nt(int, const struct iovec *, size_t, ssize_t) hidden; -int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -bool __is_linux_2_6_23(void) hidden; -int64_t __fix_enotdir(int64_t, char16_t *) hidden; -int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *) hidden; bool _check_interrupts(bool, struct Fd *) hidden; -void _check_sigchld(void) hidden; -void _check_sigalrm(void) hidden; -int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool) hidden; -bool isdirectory_nt(const char *) hidden; -bool isregularfile_nt(const char *) hidden; -bool issymlink_nt(const char *) hidden; -bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; -char16_t *CreatePipeName(char16_t *) hidden; -int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]) hidden; -int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int) hidden; -int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]) hidden; int sys_clock_gettime_nt(int, struct timespec *) hidden; -int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; -int sys_getsetpriority_nt(int, int, int, int (*)(int)); -int64_t __winerr(void) nocallback privileged; -int64_t ntreturn(uint32_t); ssize_t sys_readv_nt(struct Fd *, const struct iovec *, int) hidden; ssize_t sys_writev_nt(int, const struct iovec *, int) hidden; unsigned __wincrash_nt(struct NtExceptionPointers *); -void *GetProcAddressModule(const char *, const char *) hidden; -void WinMainForked(void) hidden; void _ntcontext2linux(struct ucontext *, const struct NtContext *) hidden; void _ntlinux2context(struct NtContext *, const ucontext_t *) hidden; struct NtOverlapped *_offset2overlap(int64_t, int64_t, diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index 1e103539b..458f5a93a 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -18,10 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/weaken.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/strace.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/lockcmpxchgp.h" #include "libc/nexgen32e/threaded.h" diff --git a/libc/calls/ioctl_default.c b/libc/calls/ioctl_default.c index 11886fb1a..9df7412fa 100644 --- a/libc/calls/ioctl_default.c +++ b/libc/calls/ioctl_default.c @@ -19,6 +19,8 @@ #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/ioctl_fioclex.c b/libc/calls/ioctl_fioclex.c index 8c0de779c..9eca2f40a 100644 --- a/libc/calls/ioctl_fioclex.c +++ b/libc/calls/ioctl_fioclex.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/ioctl_siocgifconf-nt.c b/libc/calls/ioctl_siocgifconf-nt.c index d693df24c..05ef547a0 100644 --- a/libc/calls/ioctl_siocgifconf-nt.c +++ b/libc/calls/ioctl_siocgifconf-nt.c @@ -19,7 +19,6 @@ #include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/intrin/cmpxchg.h" #include "libc/nt/errors.h" #include "libc/nt/iphlpapi.h" diff --git a/libc/calls/ioctl_siocgifconf.c b/libc/calls/ioctl_siocgifconf.c index 4f395736c..d9752d391 100644 --- a/libc/calls/ioctl_siocgifconf.c +++ b/libc/calls/ioctl_siocgifconf.c @@ -19,9 +19,10 @@ #include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/bits/weaken.h" -#include "libc/calls/internal.h" #include "libc/calls/ioctl.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/mem/mem.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/str/str.h" diff --git a/libc/calls/ioctl_tcgets.c b/libc/calls/ioctl_tcgets.c index 14860ee24..2a27d4987 100644 --- a/libc/calls/ioctl_tcgets.c +++ b/libc/calls/ioctl_tcgets.c @@ -20,10 +20,12 @@ #include "libc/calls/ioctl.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metatermios.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.internal.h" #include "libc/calls/ttydefaults.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/ioctl_tcsets.c b/libc/calls/ioctl_tcsets.c index 34e2592fa..77fc8b76e 100644 --- a/libc/calls/ioctl_tcsets.c +++ b/libc/calls/ioctl_tcsets.c @@ -20,6 +20,7 @@ #include "libc/calls/ioctl.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metatermios.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/ioctl_tiocgwinsz-nt.c b/libc/calls/ioctl_tiocgwinsz-nt.c index 53dafe89c..c6805d11b 100644 --- a/libc/calls/ioctl_tiocgwinsz-nt.c +++ b/libc/calls/ioctl_tiocgwinsz-nt.c @@ -19,9 +19,11 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/spinlock.h" #include "libc/log/log.h" #include "libc/nt/console.h" diff --git a/libc/calls/ioctl_tiocgwinsz.c b/libc/calls/ioctl_tiocgwinsz.c index 91abae9f8..62bc117ae 100644 --- a/libc/calls/ioctl_tiocgwinsz.c +++ b/libc/calls/ioctl_tiocgwinsz.c @@ -20,6 +20,7 @@ #include "libc/calls/ioctl.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/consts/termios.h" diff --git a/libc/calls/ioctl_tiocswinsz-nt.c b/libc/calls/ioctl_tiocswinsz-nt.c index 1b80cd24f..658638c7f 100644 --- a/libc/calls/ioctl_tiocswinsz-nt.c +++ b/libc/calls/ioctl_tiocswinsz-nt.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/console.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/ioctl_tiocswinsz.c b/libc/calls/ioctl_tiocswinsz.c index dd0db0000..d6bb7b341 100644 --- a/libc/calls/ioctl_tiocswinsz.c +++ b/libc/calls/ioctl_tiocswinsz.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/consts/termios.h" diff --git a/libc/calls/iovecsize.c b/libc/calls/iovecsize.c index 21b71dcd3..d4366a32c 100644 --- a/libc/calls/iovecsize.c +++ b/libc/calls/iovecsize.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" size_t __iovec_size(const struct iovec *v, size_t n) { size_t i, sum; diff --git a/libc/calls/isatty.c b/libc/calls/isatty.c index 3498b7bc3..63e669867 100644 --- a/libc/calls/isatty.c +++ b/libc/calls/isatty.c @@ -20,6 +20,8 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/sysv/consts/termios.h" diff --git a/libc/calls/ischardev.c b/libc/calls/ischardev.c index a310e958f..9e51bf4c8 100644 --- a/libc/calls/ischardev.c +++ b/libc/calls/ischardev.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/metastat.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/nt/enum/filetype.h" diff --git a/libc/calls/isdirectory-nt.c b/libc/calls/isdirectory-nt.c index d0579a967..a986333e4 100644 --- a/libc/calls/isdirectory-nt.c +++ b/libc/calls/isdirectory-nt.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/files.h" diff --git a/libc/calls/isdirectory.c b/libc/calls/isdirectory.c index c68af54d4..84262b49d 100644 --- a/libc/calls/isdirectory.c +++ b/libc/calls/isdirectory.c @@ -18,10 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.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/asan.internal.h" diff --git a/libc/calls/islinux.c b/libc/calls/islinux.c index 1209f959e..536545fcc 100644 --- a/libc/calls/islinux.c +++ b/libc/calls/islinux.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/sysv/consts/pr.h" diff --git a/libc/calls/isregularfile-nt.c b/libc/calls/isregularfile-nt.c index 45614ef64..959623e00 100644 --- a/libc/calls/isregularfile-nt.c +++ b/libc/calls/isregularfile-nt.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/files.h" diff --git a/libc/calls/isregularfile.c b/libc/calls/isregularfile.c index 35b145b9c..b7112bced 100644 --- a/libc/calls/isregularfile.c +++ b/libc/calls/isregularfile.c @@ -18,9 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metastat.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/asan.internal.h" diff --git a/libc/calls/issymlink-nt.c b/libc/calls/issymlink-nt.c index 4c074cadb..a474d4134 100644 --- a/libc/calls/issymlink-nt.c +++ b/libc/calls/issymlink-nt.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/files.h" diff --git a/libc/calls/issymlink.c b/libc/calls/issymlink.c index 2e2ddcfb5..9ff983f40 100644 --- a/libc/calls/issymlink.c +++ b/libc/calls/issymlink.c @@ -18,10 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.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/asan.internal.h" diff --git a/libc/calls/kill.c b/libc/calls/kill.c index a77ff7564..58caee509 100644 --- a/libc/calls/kill.c +++ b/libc/calls/kill.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/str/str.h" diff --git a/libc/calls/killpg.c b/libc/calls/killpg.c index 6808fcd13..4749efe45 100644 --- a/libc/calls/killpg.c +++ b/libc/calls/killpg.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/calls/internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/calls/lchown.c b/libc/calls/lchown.c index fabb0b4c2..b435d42f1 100644 --- a/libc/calls/lchown.c +++ b/libc/calls/lchown.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/sysv/consts/at.h" /** diff --git a/libc/calls/link.c b/libc/calls/link.c index 4ce59ad65..237a3643d 100644 --- a/libc/calls/link.c +++ b/libc/calls/link.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/linkat-nt.c b/libc/calls/linkat-nt.c index 072c203f6..94d6efae1 100644 --- a/libc/calls/linkat-nt.c +++ b/libc/calls/linkat-nt.c @@ -17,7 +17,7 @@ │ 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/files.h" #include "libc/nt/runtime.h" diff --git a/libc/calls/linkat.c b/libc/calls/linkat.c index 7b6b86888..4f02f6286 100644 --- a/libc/calls/linkat.c +++ b/libc/calls/linkat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index da27f64cf..ca5c3cbf5 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #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/sysv/errfuns.h" diff --git a/libc/calls/lseek.c b/libc/calls/lseek.c index 07961309f..7b8daec86 100644 --- a/libc/calls/lseek.c +++ b/libc/calls/lseek.c @@ -20,6 +20,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/log/backtrace.internal.h" #include "libc/zipos/zipos.internal.h" diff --git a/libc/calls/madvise-nt.c b/libc/calls/madvise-nt.c index ce50c0002..6686a4d5f 100644 --- a/libc/calls/madvise-nt.c +++ b/libc/calls/madvise-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/macros.internal.h" #include "libc/nt/enum/offerpriority.h" #include "libc/nt/memory.h" diff --git a/libc/calls/madvise.c b/libc/calls/madvise.c index d4f2c7b51..2610d9968 100644 --- a/libc/calls/madvise.c +++ b/libc/calls/madvise.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/metaflock.c b/libc/calls/metaflock.c index a13b891cc..2874c77e4 100644 --- a/libc/calls/metaflock.c +++ b/libc/calls/metaflock.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/flock.h" +#include "libc/dce.h" union metaflock { struct flock cosmo; diff --git a/libc/calls/mincore.c b/libc/calls/mincore.c index 16b9cf1f4..053fbd4e1 100644 --- a/libc/calls/mincore.c +++ b/libc/calls/mincore.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Tells you which pages are resident in memory. diff --git a/libc/calls/mkdir.c b/libc/calls/mkdir.c index d41c67814..01a42f386 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" diff --git a/libc/calls/mkdirat-nt.c b/libc/calls/mkdirat-nt.c index 16133342a..5faa232e8 100644 --- a/libc/calls/mkdirat-nt.c +++ b/libc/calls/mkdirat-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/mkdirat.c b/libc/calls/mkdirat.c index 67c5536fc..cc42bbeb5 100644 --- a/libc/calls/mkdirat.c +++ b/libc/calls/mkdirat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/mkfifo.c b/libc/calls/mkfifo.c index 2f85e7251..fece40a90 100644 --- a/libc/calls/mkfifo.c +++ b/libc/calls/mkfifo.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/nt/ipc.h" diff --git a/libc/calls/mknod.c b/libc/calls/mknod.c index 63d2b9715..c677fa0cc 100644 --- a/libc/calls/mknod.c +++ b/libc/calls/mknod.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/consts/s.h" diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index a2a36a28a..e69732c60 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/macros.internal.h" #include "libc/nt/systeminfo.h" #include "libc/str/oldutf16.internal.h" diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index f2258721b..204db959a 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" #include "libc/str/str.h" diff --git a/libc/calls/munmap-sysv.c b/libc/calls/munmap-sysv.c index d2ea45e00..95b697034 100644 --- a/libc/calls/munmap-sysv.c +++ b/libc/calls/munmap-sysv.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/runtime/directmap.internal.h" #include "libc/runtime/memtrack.internal.h" diff --git a/libc/calls/nanosleep-nt.c b/libc/calls/nanosleep-nt.c index 275bf3086..4de595513 100644 --- a/libc/calls/nanosleep-nt.c +++ b/libc/calls/nanosleep-nt.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/errno.h" #include "libc/limits.h" diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index 226eeba81..da5f71860 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/now.c b/libc/calls/now.c index e32879fd5..3db0d9625 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -21,7 +21,9 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" diff --git a/libc/calls/ntaccesscheck.c b/libc/calls/ntaccesscheck.c index a06f7188b..b0769a6a6 100644 --- a/libc/calls/ntaccesscheck.c +++ b/libc/calls/ntaccesscheck.c @@ -18,8 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" #include "libc/nt/enum/accessmask.h" diff --git a/libc/calls/ntcontext2linux.c b/libc/calls/ntcontext2linux.c index e21204e6f..8d527ee9e 100644 --- a/libc/calls/ntcontext2linux.c +++ b/libc/calls/ntcontext2linux.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/ucontext.h" #include "libc/log/libfatal.internal.h" #include "libc/nt/struct/context.h" diff --git a/libc/calls/ntreturn.c b/libc/calls/ntreturn.c index da63a2f0f..f6301a6b1 100644 --- a/libc/calls/ntreturn.c +++ b/libc/calls/ntreturn.c @@ -19,7 +19,6 @@ #include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/ntdll.h" -#include "libc/calls/internal.h" /** * Exitpoint for Windows NT system calls. diff --git a/libc/calls/ntsetprivilege.c b/libc/calls/ntsetprivilege.c index 8dd6994a4..79c36083f 100644 --- a/libc/calls/ntsetprivilege.c +++ b/libc/calls/ntsetprivilege.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/nt/privilege.h" #include "libc/nt/struct/tokenprivileges.h" diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index fe63fa82a..19a7c560e 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/pushpop.h" -#include "libc/calls/internal.h" #include "libc/calls/ntspawn.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/macros.internal.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" diff --git a/libc/calls/offset2overlap.c b/libc/calls/offset2overlap.c index e50d99811..0b5eb2961 100644 --- a/libc/calls/offset2overlap.c +++ b/libc/calls/offset2overlap.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/nt/struct/overlapped.h" +#include "libc/str/str.h" textwindows struct NtOverlapped *_offset2overlap(int64_t handle, int64_t opt_offset, diff --git a/libc/calls/oldbench.c b/libc/calls/oldbench.c index ee1ab4c41..3f233e32a 100644 --- a/libc/calls/oldbench.c +++ b/libc/calls/oldbench.c @@ -20,7 +20,7 @@ #include "libc/bits/initializer.internal.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/macros.internal.h" diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 706edddf3..f2b6001d3 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -16,29 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" -#include "libc/calls/strace.internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/spinlock.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/filesharemode.h" #include "libc/nt/enum/filetype.h" -#include "libc/nt/enum/fsctl.h" -#include "libc/nt/errors.h" #include "libc/nt/files.h" -#include "libc/nt/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/errfuns.h" -static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, - uint32_t flags, int32_t mode) { +static textwindows int sys_open_nt_impl(int dirfd, const char *path, + uint32_t flags, int32_t mode) { char16_t path16[PATH_MAX]; uint32_t perm, share, disp, attr; if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; @@ -48,10 +39,10 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, path16); } -static textwindows ssize_t sys_open_nt_console(int dirfd, - const struct NtMagicPaths *mp, - uint32_t flags, int32_t mode, - size_t fd) { +static textwindows int sys_open_nt_console(int dirfd, + const struct NtMagicPaths *mp, + uint32_t flags, int32_t mode, + size_t fd) { if (GetFileType(g_fds.p[STDIN_FILENO].handle) == kNtFileTypeChar && GetFileType(g_fds.p[STDOUT_FILENO].handle) == kNtFileTypeChar) { g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle; @@ -71,9 +62,9 @@ static textwindows ssize_t sys_open_nt_console(int dirfd, return fd; } -static textwindows ssize_t sys_open_nt_file(int dirfd, const char *file, - uint32_t flags, int32_t mode, - size_t fd) { +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)) != -1) { g_fds.p[fd].kind = kFdFile; g_fds.p[fd].flags = flags; @@ -84,8 +75,8 @@ static textwindows ssize_t sys_open_nt_file(int dirfd, const char *file, } } -textwindows ssize_t sys_open_nt(int dirfd, const char *file, uint32_t flags, - int32_t mode) { +textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, + int32_t mode) { int fd; ssize_t rc; _spinlock(&__fds_lock); diff --git a/libc/calls/openat-sysv.c b/libc/calls/openat-sysv.c index d9c3bc659..8088d5e59 100644 --- a/libc/calls/openat-sysv.c +++ b/libc/calls/openat-sysv.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/str/str.h" diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 19968a080..0ce7c46f6 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -19,7 +19,10 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/pause.c b/libc/calls/pause.c index b58303702..23886a070 100644 --- a/libc/calls/pause.c +++ b/libc/calls/pause.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/nt/synchronization.h" diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index b8d554b57..c38e6c8c7 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -17,6 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/spinlock.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" diff --git a/libc/calls/pipe-sysv.c b/libc/calls/pipe-sysv.c index 575337099..eb9da6ada 100644 --- a/libc/calls/pipe-sysv.c +++ b/libc/calls/pipe-sysv.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" int sys_pipe(int fds[2]) { diff --git a/libc/calls/pipe.c b/libc/calls/pipe.c index bbced47d4..94f65eed1 100644 --- a/libc/calls/pipe.c +++ b/libc/calls/pipe.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/pipe2-sysv.c b/libc/calls/pipe2-sysv.c index 34cb734ae..526917b8b 100644 --- a/libc/calls/pipe2-sysv.c +++ b/libc/calls/pipe2-sysv.c @@ -16,7 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/sysv/consts/o.h" diff --git a/libc/calls/pipe2.c b/libc/calls/pipe2.c index 2c916775a..738260ae4 100644 --- a/libc/calls/pipe2.c +++ b/libc/calls/pipe2.c @@ -16,8 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/consts/o.h" diff --git a/libc/calls/poll-metal.c b/libc/calls/poll-metal.c index 60c77a423..d3f9c93a7 100644 --- a/libc/calls/poll-metal.c +++ b/libc/calls/poll-metal.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/calls/internal.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/runtime/pc.internal.h" diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index c1a282992..c6b575f88 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -22,6 +22,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" diff --git a/libc/calls/poll.c b/libc/calls/poll.c index 6fb4d8ae0..052674750 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" diff --git a/libc/calls/posix_openpt.c b/libc/calls/posix_openpt.c index 13fedf2f2..93bd3309f 100644 --- a/libc/calls/posix_openpt.c +++ b/libc/calls/posix_openpt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" diff --git a/libc/calls/pread.c b/libc/calls/pread.c index 1f4370e63..6361e17b3 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -22,6 +22,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/macros.internal.h" diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 4b527d88c..2a8d7c0ce 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -20,8 +20,10 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index d9ff0a691..f3a45a039 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" diff --git a/libc/calls/ptrace.c b/libc/calls/ptrace.c index 8eb15c3c9..a2e8d2c62 100644 --- a/libc/calls/ptrace.c +++ b/libc/calls/ptrace.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/sysv/consts/ptrace.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/pwrite.c b/libc/calls/pwrite.c index 0335cbe28..c202ae0f8 100644 --- a/libc/calls/pwrite.c +++ b/libc/calls/pwrite.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/macros.internal.h" diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index 3d8744415..4a90d8201 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/raise.c b/libc/calls/raise.c index 6b47956d6..57481ec80 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -18,9 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/getconsolectrlevent.internal.h" -#include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/kprintf.h" #include "libc/nt/console.h" #include "libc/nt/errors.h" diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 9c2d44692..6bbc9ded3 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" diff --git a/libc/calls/read.c b/libc/calls/read.c index 31ffff570..2f42ea22a 100644 --- a/libc/calls/read.c +++ b/libc/calls/read.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" diff --git a/libc/calls/readansi.c b/libc/calls/readansi.c index e7ee617fb..854ad59cd 100644 --- a/libc/calls/readansi.c +++ b/libc/calls/readansi.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/str/thompike.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index e55136d35..e8f730dc2 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/mem/alloca.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/creationdisposition.h" diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index 4e90745ab..b06d285e3 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/weaken.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/readv-nt.c b/libc/calls/readv-nt.c index a9b55edd5..e4aa2ebf7 100644 --- a/libc/calls/readv-nt.c +++ b/libc/calls/readv-nt.c @@ -19,6 +19,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" textwindows ssize_t sys_readv_nt(struct Fd *fd, const struct iovec *iov, diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 54ae7ee12..d54ddbe48 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" diff --git a/libc/calls/reboot.c b/libc/calls/reboot.c index 3077f7be5..6f422393c 100644 --- a/libc/calls/reboot.c +++ b/libc/calls/reboot.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/framebuffervirtualscreeninfo.h" #include "libc/dce.h" #include "libc/nt/enum/version.h" diff --git a/libc/calls/renameat-nt.c b/libc/calls/renameat-nt.c index 949d05d84..8b094783d 100644 --- a/libc/calls/renameat-nt.c +++ b/libc/calls/renameat-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/enum/movefileexflags.h" #include "libc/nt/files.h" diff --git a/libc/calls/renameat.c b/libc/calls/renameat.c index 107ef3b69..f3084e9bc 100644 --- a/libc/calls/renameat.c +++ b/libc/calls/renameat.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index 81a0d45c9..528b25e4b 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -20,6 +20,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" diff --git a/libc/calls/rusage2linux.c b/libc/calls/rusage2linux.c index 855fd8dbe..d1b62b8e9 100644 --- a/libc/calls/rusage2linux.c +++ b/libc/calls/rusage2linux.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/rusage.h" void __rusage2linux(struct rusage *ru) { if (IsXnu()) { diff --git a/libc/calls/sched_getaffinity.c b/libc/calls/sched_getaffinity.c index 023b217c3..cc9362e95 100644 --- a/libc/calls/sched_getaffinity.c +++ b/libc/calls/sched_getaffinity.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/str/str.h" /** diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index 99d6db694..3d917e045 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/nt/enum/processaccess.h" diff --git a/libc/calls/setgid.c b/libc/calls/setgid.c index 93431f3a4..35cbe11f8 100644 --- a/libc/calls/setgid.c +++ b/libc/calls/setgid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets group id of current process. diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c index 3dd838e30..32e8e4fa4 100644 --- a/libc/calls/setitimer-nt.c +++ b/libc/calls/setitimer-nt.c @@ -19,7 +19,6 @@ #include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/itimerval.h" diff --git a/libc/calls/setpgid.c b/libc/calls/setpgid.c index 36a2aad4c..24f6591fd 100644 --- a/libc/calls/setpgid.c +++ b/libc/calls/setpgid.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/nt/console.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/setpriority-nt.c b/libc/calls/setpriority-nt.c index cb762d0eb..aadb52c2e 100644 --- a/libc/calls/setpriority-nt.c +++ b/libc/calls/setpriority-nt.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" -#include "libc/calls/internal.h" #include "libc/calls/kntprioritycombos.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" diff --git a/libc/calls/setpriority.c b/libc/calls/setpriority.c index 682e62e54..10d8bd26a 100644 --- a/libc/calls/setpriority.c +++ b/libc/calls/setpriority.c @@ -17,7 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets nice value of thing. diff --git a/libc/calls/setregid.c b/libc/calls/setregid.c index e2ee4dc94..0134b45f6 100644 --- a/libc/calls/setregid.c +++ b/libc/calls/setregid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets real and/or effective group ids. diff --git a/libc/calls/setresgid.c b/libc/calls/setresgid.c index 846df7a93..04601eccd 100644 --- a/libc/calls/setresgid.c +++ b/libc/calls/setresgid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets real, effective, and "saved" group ids. diff --git a/libc/calls/setresuid.c b/libc/calls/setresuid.c index a585dc91c..8e05da8cf 100644 --- a/libc/calls/setresuid.c +++ b/libc/calls/setresuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets real, effective, and "saved" user ids. diff --git a/libc/calls/setreuid.c b/libc/calls/setreuid.c index e3ff17143..9e9e4f14c 100644 --- a/libc/calls/setreuid.c +++ b/libc/calls/setreuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets real and/or effective user ids. diff --git a/libc/calls/setsid.c b/libc/calls/setsid.c index adcfff21b..c63b255cf 100644 --- a/libc/calls/setsid.c +++ b/libc/calls/setsid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Creates session and sets the process group id. diff --git a/libc/calls/setuid.c b/libc/calls/setuid.c index f46735559..abaa567b8 100644 --- a/libc/calls/setuid.c +++ b/libc/calls/setuid.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Sets user id of current process. diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 5412f0bf5..9c8717b6d 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -16,8 +16,8 @@ │ 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/spinlock.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/sig.h" diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index d94d02e18..2aba4946f 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -16,9 +16,9 @@ │ 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/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/lockcmpxchg.h" diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 73f8fd4d3..0270839d6 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -22,6 +22,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/sigaction-linux.internal.h" @@ -29,6 +30,8 @@ #include "libc/calls/struct/sigaction-openbsd.internal.h" #include "libc/calls/struct/sigaction-xnu.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c index 4839b9dc3..d150d27ac 100644 --- a/libc/calls/sigaltstack.c +++ b/libc/calls/sigaltstack.c @@ -16,10 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/metasigaltstack.h" #include "libc/calls/struct/sigaltstack.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c index c17804392..14c278bcc 100644 --- a/libc/calls/sigchld-nt.c +++ b/libc/calls/sigchld-nt.c @@ -19,7 +19,9 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/spinlock.h" #include "libc/nt/enum/wait.h" diff --git a/libc/calls/sigenter-freebsd.c b/libc/calls/sigenter-freebsd.c index 7afbb42d5..4bc1d2aa9 100644 --- a/libc/calls/sigenter-freebsd.c +++ b/libc/calls/sigenter-freebsd.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/siginfo-freebsd.internal.h" #include "libc/calls/struct/siginfo.h" diff --git a/libc/calls/sigenter-netbsd.c b/libc/calls/sigenter-netbsd.c index 50a1a93d8..b1531596c 100644 --- a/libc/calls/sigenter-netbsd.c +++ b/libc/calls/sigenter-netbsd.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/siginfo-netbsd.internal.h" #include "libc/calls/struct/siginfo.h" diff --git a/libc/calls/sigenter-openbsd.c b/libc/calls/sigenter-openbsd.c index 253d61f50..cd503f4de 100644 --- a/libc/calls/sigenter-openbsd.c +++ b/libc/calls/sigenter-openbsd.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/siginfo-openbsd.internal.h" #include "libc/calls/struct/siginfo.h" diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index 46a2fb831..03fdc9a97 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/metasigaltstack.h" #include "libc/calls/struct/siginfo-xnu.internal.h" #include "libc/calls/struct/siginfo.h" diff --git a/libc/calls/sigprocmask-sysv.greg.c b/libc/calls/sigprocmask-sysv.greg.c index 7f6ce2b41..625808d8e 100644 --- a/libc/calls/sigprocmask-sysv.greg.c +++ b/libc/calls/sigprocmask-sysv.greg.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/sigset.h" int sys_sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) { diff --git a/libc/calls/sleep.c b/libc/calls/sleep.c index 19810c82f..f4dea02a5 100644 --- a/libc/calls/sleep.c +++ b/libc/calls/sleep.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/timespec.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/splice.c b/libc/calls/splice.c index 08cf2d8fc..35f8a0710 100644 --- a/libc/calls/splice.c +++ b/libc/calls/splice.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #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/sysv/errfuns.h" diff --git a/libc/calls/stat2cosmo.c b/libc/calls/stat2cosmo.c index c4e28281e..4d5e3e850 100644 --- a/libc/calls/stat2cosmo.c +++ b/libc/calls/stat2cosmo.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/metastat.internal.h" #include "libc/dce.h" diff --git a/libc/calls/state.internal.h b/libc/calls/state.internal.h new file mode 100644 index 000000000..b7a14781f --- /dev/null +++ b/libc/calls/state.internal.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +hidden extern int __vforked; +hidden extern int __fds_lock; +hidden extern int __sig_lock; +hidden extern bool __time_critical; +hidden extern unsigned __sighandrvas[NSIG]; +hidden extern unsigned __sighandflags[NSIG]; +hidden extern const struct NtSecurityAttributes kNtIsInheritable; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ */ diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index 1cbcc3907..ad5149afa 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/once.h" #include "libc/intrin/spinlock.h" diff --git a/libc/calls/symlinkat.c b/libc/calls/symlinkat.c index de4540654..cfc877f2d 100644 --- a/libc/calls/symlinkat.c +++ b/libc/calls/symlinkat.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/calls/sync-nt.c b/libc/calls/sync-nt.c index 31dbc395b..4657e92d4 100644 --- a/libc/calls/sync-nt.c +++ b/libc/calls/sync-nt.c @@ -18,6 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" diff --git a/libc/calls/sync.c b/libc/calls/sync.c index 842df3762..9586a1b43 100644 --- a/libc/calls/sync.c +++ b/libc/calls/sync.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/sync_file_range.c b/libc/calls/sync_file_range.c index 54110b570..5c020e472 100644 --- a/libc/calls/sync_file_range.c +++ b/libc/calls/sync_file_range.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/errno.h" /** diff --git a/libc/calls/sys_utimes_nt.c b/libc/calls/sys_utimes_nt.c index 8a7638a14..225ecd90d 100644 --- a/libc/calls/sys_utimes_nt.c +++ b/libc/calls/sys_utimes_nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/timeval.h" #include "libc/sysv/consts/at.h" textwindows int sys_utimes_nt(const char *path, const struct timeval tv[2]) { diff --git a/libc/calls/syscall-nt.internal.h b/libc/calls/syscall-nt.internal.h new file mode 100644 index 000000000..51bba6286 --- /dev/null +++ b/libc/calls/syscall-nt.internal.h @@ -0,0 +1,43 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +bool32 sys_isatty_nt(int) hidden; +char *sys_getcwd_nt(char *, size_t) hidden; +int sys_chdir_nt(const char *) hidden; +int sys_close_epoll_nt(int) hidden; +int sys_dup_nt(int, int, int, int) hidden; +int sys_execve_nt(const char *, char *const[], char *const[]) hidden; +int sys_faccessat_nt(int, const char *, int, uint32_t) hidden; +int sys_fadvise_nt(int, uint64_t, uint64_t, int) hidden; +int sys_fchdir_nt(int) hidden; +int sys_fchmodat_nt(int, const char *, uint32_t, int) hidden; +int sys_fcntl_nt(int, int, uintptr_t) hidden; +int sys_fdatasync_nt(int) hidden; +int sys_flock_nt(int, int) hidden; +int sys_fork_nt(void) hidden; +int sys_ftruncate_nt(int64_t, uint64_t) hidden; +int sys_getppid_nt(void) hidden; +int sys_getpriority_nt(int) hidden; +int sys_getsetpriority_nt(int, int, int, int (*)(int)); +int sys_kill_nt(int, int) hidden; +int sys_linkat_nt(int, const char *, int, const char *) hidden; +int sys_madvise_nt(void *, size_t, int) hidden; +int sys_mkdirat_nt(int, const char *, uint32_t) hidden; +int sys_msync_nt(char *, size_t, int) hidden; +int sys_open_nt(int, const char *, uint32_t, int32_t) dontdiscard hidden; +int sys_pipe_nt(int[hasatleast 2], unsigned) hidden; +int sys_renameat_nt(int, const char *, int, const char *) hidden; +int sys_sched_yield_nt(void) hidden; +int sys_setpriority_nt(int) hidden; +int sys_symlinkat_nt(const char *, int, const char *) hidden; +int sys_sync_nt(void) hidden; +int sys_truncate_nt(const char *, uint64_t) hidden; +int sys_unlinkat_nt(int, const char *, int) hidden; +int64_t sys_lseek_nt(int, int64_t, int) hidden; +ssize_t sys_readlinkat_nt(int, const char *, char *, size_t) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ */ diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h new file mode 100644 index 000000000..479b6c6ed --- /dev/null +++ b/libc/calls/syscall-sysv.internal.h @@ -0,0 +1,123 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALL_SYSV_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_SYSV_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +#define i32 int32_t +#define i64 int64_t +#define u32 uint32_t +#define u64 uint64_t +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § syscalls » system five » structless synthetic jump slots ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +axdx_t __sys_pipe(i32[hasatleast 2], i32) hidden; +axdx_t sys_fork(void) hidden; +axdx_t sys_getpid(void) hidden; +char *sys_getcwd(char *, u64) hidden; +char *sys_getcwd_xnu(char *, u64) hidden; +i32 __sys_dup3(i32, i32, i32) hidden; +i32 __sys_execve(const char *, char *const[], char *const[]) hidden; +i32 __sys_fcntl(i32, i32, ...) hidden; +i32 __sys_fstat(i32, void *) hidden; +i32 __sys_fstatat(i32, const char *, void *, i32) hidden; +i32 __sys_munmap(void *, u64) hidden; +i32 __sys_openat(i32, const char *, i32, u32) hidden; +i32 __sys_pipe2(i32[hasatleast 2], u32) hidden; +i32 sys_arch_prctl(i32, i64) hidden; +i32 sys_chdir(const char *) hidden; +i32 sys_chroot(const char *) hidden; +i32 sys_close(i32) hidden; +i32 sys_dup(i32) hidden; +i32 sys_dup2(i32, i32) hidden; +i32 sys_dup3(i32, i32, i32) hidden; +i32 sys_execve(const char *, char *const[], char *const[]) hidden; +i32 sys_faccessat(i32, const char *, i32, u32) hidden; +i32 sys_fadvise(i32, i64, i64, i32) hidden; +i32 sys_fchdir(i32) hidden; +i32 sys_fchmod(i32, u32) hidden; +i32 sys_fchmodat(i32, const char *, u32, u32) hidden; +i32 sys_fchown(i64, u32, u32) hidden; +i32 sys_fchownat(i32, const char *, u32, u32, u32) hidden; +i32 sys_fcntl(i32, i32, u64) hidden; +i32 sys_fdatasync(i32) hidden; +i32 sys_flock(i32, i32) hidden; +i32 sys_fsync(i32) hidden; +i32 sys_ftruncate(i32, i64, i64) hidden; +i32 sys_getcontext(void *) hidden; +i32 sys_getpgid(i32) hidden; +i32 sys_getpgrp(void) hidden; +i32 sys_getppid(void) hidden; +i32 sys_getpriority(i32, u32) hidden; +i32 sys_getresgid(u32 *, u32 *, u32 *); +i32 sys_getresuid(u32 *, u32 *, u32 *); +i32 sys_getsid(int) hidden; +i32 sys_ioctl(i32, u64, ...) hidden; +i32 sys_kill(i32, i32, i32) hidden; +i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden; +i32 sys_lseek(i32, i64, i64, i64) hidden; +i32 sys_madvise(void *, size_t, i32) hidden; +i32 sys_memfd_create(const char *, u32) hidden; +i32 sys_mincore(void *, u64, unsigned char *) hidden; +i32 sys_mkdirat(i32, const char *, u32) hidden; +i32 sys_mkfifo(const char *, u32) hidden; +i32 sys_mknod(const char *, u32, u64) hidden; +i32 sys_mprotect(void *, u64, i32) hidden; +i32 sys_msync(void *, u64, i32) hidden; +i32 sys_munmap(void *, u64) hidden; +i32 sys_openat(i32, const char *, i32, u32) hidden; +i32 sys_pause(void) hidden; +i32 sys_pipe(i32[hasatleast 2]) hidden; +i32 sys_pipe2(i32[hasatleast 2], u32) hidden; +i32 sys_pledge(const char *, const char *) hidden; +i32 sys_posix_openpt(i32) hidden; +i32 sys_renameat(i32, const char *, i32, const char *) hidden; +i32 sys_sched_setaffinity(i32, u64, const void *) hidden; +i32 sys_sched_yield(void) hidden; +i32 sys_setgid(i32) hidden; +i32 sys_setpgid(i32, i32) hidden; +i32 sys_setpriority(i32, u32, i32) hidden; +i32 sys_setregid(u32, u32) hidden; +i32 sys_setresgid(u32, u32, u32) hidden; +i32 sys_setresuid(u32, u32, u32) hidden; +i32 sys_setreuid(u32, u32) hidden; +i32 sys_setsid(void) hidden; +i32 sys_setuid(i32) hidden; +i32 sys_sigaction(i32, const void *, void *, i64, i64) hidden; +i32 sys_sigaltstack(const void *, void *) hidden; +i32 sys_symlinkat(const char *, i32, const char *) hidden; +i32 sys_sync(void) hidden; +i32 sys_sync_file_range(i32, i64, i64, u32) hidden; +i32 sys_tgkill(i32, i32, i32) hidden; +i32 sys_tkill(i32, i32, void *) hidden; +i32 sys_truncate(const char *, u64, u64) hidden; +i32 sys_uname(char *) hidden; +i32 sys_unlinkat(i32, const char *, i32) hidden; +i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden; +i64 sys_getrandom(void *, u64, u32) hidden; +i64 sys_pread(i32, void *, u64, i64, i64) hidden; +i64 sys_ptrace(int, i32, void *, void *) hidden; +i64 sys_pwrite(i32, const void *, u64, i64, i64) hidden; +i64 sys_read(i32, void *, u64) hidden; +i64 sys_readlink(const char *, char *, u64) hidden; +i64 sys_readlinkat(int, const char *, char *, u64) hidden; +i64 sys_sched_getaffinity(i32, u64, void *) hidden; +i64 sys_sendfile(i32, i32, i64 *, u64) hidden; +i64 sys_splice(i32, i64 *, i32, i64 *, u64, u32) hidden; +i64 sys_write(i32, const void *, u64) hidden; +u32 sys_getegid(void) hidden; +u32 sys_geteuid(void) hidden; +u32 sys_getgid(void) hidden; +u32 sys_gettid(void) hidden; +u32 sys_getuid(void) hidden; +u32 sys_umask(u32) hidden; +void *__sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden; +void *sys_mremap(void *, u64, u64, i32, void *) hidden; +void sys_exit(int) hidden; + +#undef i32 +#undef i64 +#undef u32 +#undef u64 +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_SYSV_INTERNAL_H_ */ diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h new file mode 100644 index 000000000..d4beaee79 --- /dev/null +++ b/libc/calls/syscall_support-nt.internal.h @@ -0,0 +1,27 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_NT_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_NT_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +bool isdirectory_nt(const char *) hidden; +bool isregularfile_nt(const char *) hidden; +bool issymlink_nt(const char *) hidden; +bool32 ntsetprivilege(int64_t, const char16_t *, uint32_t) hidden; +char16_t *CreatePipeName(char16_t *) hidden; +int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]) hidden; +int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int) hidden; +int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]) hidden; +int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool) hidden; +int ntaccesscheck(const char16_t *, uint32_t) paramsnonnull() hidden; +int64_t __fix_enotdir(int64_t, char16_t *) hidden; +int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *) hidden; +int64_t __winerr(void) nocallback privileged; +int64_t ntreturn(uint32_t); +void *GetProcAddressModule(const char *, const char *) hidden; +void WinMainForked(void) hidden; +void _check_sigalrm(void) hidden; +void _check_sigchld(void) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_NT_INTERNAL_H_ */ diff --git a/libc/calls/syscall_support-sysv.internal.h b/libc/calls/syscall_support-sysv.internal.h new file mode 100644 index 000000000..f39cf80bd --- /dev/null +++ b/libc/calls/syscall_support-sysv.internal.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_SYSV_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_SYSV_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § syscalls » system five » structless support ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +bool __is_linux_2_6_23(void) hidden; +int __fixupnewfd(int, int) hidden; +int __notziposat(int, const char *); +int gethostname_bsd(char *, size_t) hidden; +int gethostname_linux(char *, size_t) hidden; +int gethostname_nt(char *, size_t, int) hidden; +void *__get_clock_gettime(void) hidden; +void *__vdsofunc(const char *) hidden; +void __onfork(void) hidden; +void __restore_rt() hidden; +void __restore_rt_netbsd(void) hidden; +void cosmo2flock(uintptr_t); +void flock2cosmo(uintptr_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_SYSV_INTERNAL_H_ */ diff --git a/libc/calls/sysinfo-nt.c b/libc/calls/sysinfo-nt.c index 934d4fafc..2ee862ca9 100644 --- a/libc/calls/sysinfo-nt.c +++ b/libc/calls/sysinfo-nt.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/loadavg.internal.h" #include "libc/calls/struct/sysinfo.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/accounting.h" #include "libc/nt/struct/memorystatusex.h" #include "libc/nt/struct/systeminfo.h" diff --git a/libc/calls/sysinfo.c b/libc/calls/sysinfo.c index 18b33083a..d04038f10 100644 --- a/libc/calls/sysinfo.c +++ b/libc/calls/sysinfo.c @@ -16,16 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/sysinfo.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" -#include "libc/nt/accounting.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/memorystatusex.h" -#include "libc/nt/systeminfo.h" +#include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/tcdrain.c b/libc/calls/tcdrain.c index f6220eb98..c7ca74c3a 100644 --- a/libc/calls/tcdrain.c +++ b/libc/calls/tcdrain.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/termios.h" int tcdrain(int fd) { diff --git a/libc/calls/tcflow.c b/libc/calls/tcflow.c index 9468cafb3..ec1152d90 100644 --- a/libc/calls/tcflow.c +++ b/libc/calls/tcflow.c @@ -16,9 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/tcflush.c b/libc/calls/tcflush.c index fcef3676d..6229b6c68 100644 --- a/libc/calls/tcflush.c +++ b/libc/calls/tcflush.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" /** diff --git a/libc/calls/tcgetsid.c b/libc/calls/tcgetsid.c index 8bdbdc04c..d69a1aa6e 100644 --- a/libc/calls/tcgetsid.c +++ b/libc/calls/tcgetsid.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/sysv/consts/termios.h" diff --git a/libc/calls/tcsendbreak.c b/libc/calls/tcsendbreak.c index 2f8299a9d..8a9e5228f 100644 --- a/libc/calls/tcsendbreak.c +++ b/libc/calls/tcsendbreak.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/sysv/consts/termios.h" diff --git a/libc/calls/tgkill.c b/libc/calls/tgkill.c index a729b4c6d..214be57e8 100644 --- a/libc/calls/tgkill.c +++ b/libc/calls/tgkill.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" /** * Kills thread group. diff --git a/libc/calls/timecritical.c b/libc/calls/timecritical.c index 7f32a4b50..8215b1eec 100644 --- a/libc/calls/timecritical.c +++ b/libc/calls/timecritical.c @@ -16,6 +16,5 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" bool __time_critical; diff --git a/libc/calls/tkill.c b/libc/calls/tkill.c index 69c9576ae..0d20432de 100644 --- a/libc/calls/tkill.c +++ b/libc/calls/tkill.c @@ -17,8 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/nt/enum/threadaccess.h" diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index 29c0819b5..71f9c2f4f 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -16,7 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" diff --git a/libc/calls/truncate.c b/libc/calls/truncate.c index a8c3f75f1..752116066 100644 --- a/libc/calls/truncate.c +++ b/libc/calls/truncate.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" -#include "libc/sysv/errfuns.h" /** * Reduces or extends underlying physical medium of file. diff --git a/libc/calls/ttyname_r.c b/libc/calls/ttyname_r.c index fcef19b89..900895c48 100644 --- a/libc/calls/ttyname_r.c +++ b/libc/calls/ttyname_r.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" diff --git a/libc/calls/umask.c b/libc/calls/umask.c index a6a439f8c..b1b932ecd 100644 --- a/libc/calls/umask.c +++ b/libc/calls/umask.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" /** diff --git a/libc/calls/uname.c b/libc/calls/uname.c index a7b22023e..24f88ff51 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -18,9 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/utsname.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/calls/unlinkat-nt.c b/libc/calls/unlinkat-nt.c index 3b8555f03..370afe79e 100644 --- a/libc/calls/unlinkat-nt.c +++ b/libc/calls/unlinkat-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" @@ -30,6 +30,7 @@ #include "libc/nt/struct/win32fileattributedata.h" #include "libc/nt/struct/win32finddata.h" #include "libc/nt/synchronization.h" +#include "libc/str/str.h" #include "libc/sysv/consts/at.h" /** diff --git a/libc/calls/unlinkat.c b/libc/calls/unlinkat.c index edc626d35..996bc6962 100644 --- a/libc/calls/unlinkat.c +++ b/libc/calls/unlinkat.c @@ -18,12 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" diff --git a/libc/calls/usleep.c b/libc/calls/usleep.c index c51e26fcb..5160fb372 100644 --- a/libc/calls/usleep.c +++ b/libc/calls/usleep.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/timespec.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 0a5972cde..00b3e6d3d 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/conv.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" diff --git a/libc/calls/vdprintf.c b/libc/calls/vdprintf.c index cf4e92fdd..652d9b9dc 100644 --- a/libc/calls/vdprintf.c +++ b/libc/calls/vdprintf.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" diff --git a/libc/calls/vdsofunc.greg.c b/libc/calls/vdsofunc.greg.c index 843b45822..9e1e1fa40 100644 --- a/libc/calls/vdsofunc.greg.c +++ b/libc/calls/vdsofunc.greg.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/elf/scalar.h" #include "libc/elf/struct/ehdr.h" #include "libc/elf/struct/shdr.h" diff --git a/libc/calls/wait3.c b/libc/calls/wait3.c index c236e77af..2f9e44c3d 100644 --- a/libc/calls/wait3.c +++ b/libc/calls/wait3.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index 0d4fc2f5e..7bd1990b6 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -21,8 +21,10 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/rusage.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/conv.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/spinlock.h" diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index 7c850d6ec..d79500212 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" diff --git a/libc/calls/winerr.internal.h b/libc/calls/winerr.internal.h new file mode 100644 index 000000000..e71eb74a9 --- /dev/null +++ b/libc/calls/winerr.internal.h @@ -0,0 +1,8 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ */ diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 5ddc51988..46110e534 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/runtime.h" diff --git a/libc/calls/write.c b/libc/calls/write.c index 7bcc3a7d5..5cfbcc581 100644 --- a/libc/calls/write.c +++ b/libc/calls/write.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sock/sock.h" diff --git a/libc/calls/writev-metal.c b/libc/calls/writev-metal.c index 3788b103f..596a564e8 100644 --- a/libc/calls/writev-metal.c +++ b/libc/calls/writev-metal.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" #include "libc/sysv/errfuns.h" ssize_t sys_writev_metal(struct Fd *fd, const struct iovec *iov, int iovlen) { diff --git a/libc/calls/writev-serial.c b/libc/calls/writev-serial.c index 0fc1e5aaf..022df1d5f 100644 --- a/libc/calls/writev-serial.c +++ b/libc/calls/writev-serial.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/runtime/pc.internal.h" diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 0f574b732..e2998dfc3 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -20,6 +20,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/dns/getntnameservers.c b/libc/dns/getntnameservers.c index 7181cb13d..b906cd7a8 100644 --- a/libc/dns/getntnameservers.c +++ b/libc/dns/getntnameservers.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dns/dns.h" #include "libc/dns/resolvconf.h" #include "libc/nt/enum/keyaccess.h" diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 664f7e396..cb789cc0c 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -21,7 +21,7 @@ #include "libc/bits/likely.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/dce.h" diff --git a/libc/intrin/closehandle.c b/libc/intrin/closehandle.c index dfd278caf..5d32fc810 100644 --- a/libc/intrin/closehandle.c +++ b/libc/intrin/closehandle.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/log/log.h" #include "libc/nt/runtime.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/createdirectory.c b/libc/intrin/createdirectory.c index f0a8efd10..1a3884279 100644 --- a/libc/intrin/createdirectory.c +++ b/libc/intrin/createdirectory.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/createfile.c b/libc/intrin/createfile.c index 6290f7381..31e3fef92 100644 --- a/libc/intrin/createfile.c +++ b/libc/intrin/createfile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/createfilemapping.c b/libc/intrin/createfilemapping.c index a8a5ea4aa..7e149b5e8 100644 --- a/libc/intrin/createfilemapping.c +++ b/libc/intrin/createfilemapping.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/memory.h" #include "libc/nt/struct/securityattributes.h" diff --git a/libc/intrin/createfilemappingnuma.c b/libc/intrin/createfilemappingnuma.c index 7da20c4fc..33a51289e 100644 --- a/libc/intrin/createfilemappingnuma.c +++ b/libc/intrin/createfilemappingnuma.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/memory.h" diff --git a/libc/intrin/createnamedpipe.c b/libc/intrin/createnamedpipe.c index 7e70683e1..4c6d7a7ea 100644 --- a/libc/intrin/createnamedpipe.c +++ b/libc/intrin/createnamedpipe.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/ipc.h" #include "libc/nt/struct/securityattributes.h" diff --git a/libc/intrin/createpipe.c b/libc/intrin/createpipe.c index 4e295530a..a8bb75b2e 100644 --- a/libc/intrin/createpipe.c +++ b/libc/intrin/createpipe.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/ipc.h" #include "libc/nt/struct/securityattributes.h" diff --git a/libc/intrin/createprocess.c b/libc/intrin/createprocess.c index e18232d85..13a7b05c2 100644 --- a/libc/intrin/createprocess.c +++ b/libc/intrin/createprocess.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/process.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/createsymboliclink.c b/libc/intrin/createsymboliclink.c index ddbe879a0..18bc51c8b 100644 --- a/libc/intrin/createsymboliclink.c +++ b/libc/intrin/createsymboliclink.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/files.h" diff --git a/libc/intrin/createthread.c b/libc/intrin/createthread.c index 77992f157..d9ebf9650 100644 --- a/libc/intrin/createthread.c +++ b/libc/intrin/createthread.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/struct/securityattributes.h" #include "libc/nt/thread.h" diff --git a/libc/intrin/deletefile.c b/libc/intrin/deletefile.c index bf30eb534..c9284a6de 100644 --- a/libc/intrin/deletefile.c +++ b/libc/intrin/deletefile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/describentsecurityattributes.greg.c b/libc/intrin/describentsecurityattributes.greg.c index 192fb72f0..141483677 100644 --- a/libc/intrin/describentsecurityattributes.greg.c +++ b/libc/intrin/describentsecurityattributes.greg.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/struct/securityattributes.h" diff --git a/libc/intrin/deviceiocontrol.greg.c b/libc/intrin/deviceiocontrol.greg.c index 903137913..456f8026f 100644 --- a/libc/intrin/deviceiocontrol.greg.c +++ b/libc/intrin/deviceiocontrol.greg.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/findclose.c b/libc/intrin/findclose.c index 17fe27fe7..652d4f9cd 100644 --- a/libc/intrin/findclose.c +++ b/libc/intrin/findclose.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/findfirstfile.c b/libc/intrin/findfirstfile.c index 76936932d..02a431208 100644 --- a/libc/intrin/findfirstfile.c +++ b/libc/intrin/findfirstfile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" diff --git a/libc/intrin/findnextfile.c b/libc/intrin/findnextfile.c index 750ea2138..5f3f0b08b 100644 --- a/libc/intrin/findnextfile.c +++ b/libc/intrin/findnextfile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" diff --git a/libc/intrin/flushfilebuffers.c b/libc/intrin/flushfilebuffers.c index e6b4c90e4..918f5d321 100644 --- a/libc/intrin/flushfilebuffers.c +++ b/libc/intrin/flushfilebuffers.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" __msabi extern typeof(FlushFileBuffers) *const __imp_FlushFileBuffers; diff --git a/libc/intrin/flushviewoffile.c b/libc/intrin/flushviewoffile.c index e8ad6082c..9912b3534 100644 --- a/libc/intrin/flushviewoffile.c +++ b/libc/intrin/flushviewoffile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/memory.h" __msabi extern typeof(FlushViewOfFile) *const __imp_FlushViewOfFile; diff --git a/libc/intrin/generateconsolectrlevent.c b/libc/intrin/generateconsolectrlevent.c index 4617973d2..31cfbf60f 100644 --- a/libc/intrin/generateconsolectrlevent.c +++ b/libc/intrin/generateconsolectrlevent.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/console.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/getexitcodeprocess.c b/libc/intrin/getexitcodeprocess.c index 9fd02ea1e..8a783e3a7 100644 --- a/libc/intrin/getexitcodeprocess.c +++ b/libc/intrin/getexitcodeprocess.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/accounting.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/getfileattributes.c b/libc/intrin/getfileattributes.c index c7dcc4c00..943ce0f9e 100644 --- a/libc/intrin/getfileattributes.c +++ b/libc/intrin/getfileattributes.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/files.h" diff --git a/libc/intrin/getpid.c b/libc/intrin/getpid.c index d45b03254..a0d0c032d 100644 --- a/libc/intrin/getpid.c +++ b/libc/intrin/getpid.c @@ -17,7 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/runtime/internal.h" /** diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index bf7b7021e..82f3395f2 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -22,7 +22,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/divmod10.internal.h" diff --git a/libc/intrin/mapviewoffileex.c b/libc/intrin/mapviewoffileex.c index 778e56497..3542063d1 100644 --- a/libc/intrin/mapviewoffileex.c +++ b/libc/intrin/mapviewoffileex.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/memory.h" diff --git a/libc/intrin/mapviewoffileexnuma.c b/libc/intrin/mapviewoffileexnuma.c index dbbe9e3e5..9ec6faae3 100644 --- a/libc/intrin/mapviewoffileexnuma.c +++ b/libc/intrin/mapviewoffileexnuma.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/enum/filemapflags.h" diff --git a/libc/intrin/movefileex.c b/libc/intrin/movefileex.c index 8f497ab8c..5eb10ab26 100644 --- a/libc/intrin/movefileex.c +++ b/libc/intrin/movefileex.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" diff --git a/libc/intrin/openprocess.c b/libc/intrin/openprocess.c index 897d4f7c9..44f570c19 100644 --- a/libc/intrin/openprocess.c +++ b/libc/intrin/openprocess.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/memory.h" #include "libc/nt/process.h" diff --git a/libc/intrin/prot2nt.greg.c b/libc/intrin/prot2nt.greg.c index 4547a57ee..80b7a78a3 100644 --- a/libc/intrin/prot2nt.greg.c +++ b/libc/intrin/prot2nt.greg.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/nt/enum/pageflags.h" #include "libc/runtime/directmap.internal.h" #include "libc/sysv/consts/prot.h" diff --git a/libc/intrin/releasefd.c b/libc/intrin/releasefd.c index ef08101db..48124c3cb 100644 --- a/libc/intrin/releasefd.c +++ b/libc/intrin/releasefd.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" diff --git a/libc/intrin/removedirectory.c b/libc/intrin/removedirectory.c index 6f6035c54..6d1709fe8 100644 --- a/libc/intrin/removedirectory.c +++ b/libc/intrin/removedirectory.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/reopenfile.c b/libc/intrin/reopenfile.c index b20292ae6..2aef55ebd 100644 --- a/libc/intrin/reopenfile.c +++ b/libc/intrin/reopenfile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/setcurrentdirectory.c b/libc/intrin/setcurrentdirectory.c index 28f0f9516..9045f7277 100644 --- a/libc/intrin/setcurrentdirectory.c +++ b/libc/intrin/setcurrentdirectory.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/terminateprocess.c b/libc/intrin/terminateprocess.c index 5f308f7cf..c658f1a34 100644 --- a/libc/intrin/terminateprocess.c +++ b/libc/intrin/terminateprocess.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/console.h" #include "libc/nt/runtime.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/unmapviewoffile.c b/libc/intrin/unmapviewoffile.c index aa7684fa0..455db63d3 100644 --- a/libc/intrin/unmapviewoffile.c +++ b/libc/intrin/unmapviewoffile.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/memory.h" __msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; diff --git a/libc/intrin/vforked.c b/libc/intrin/vforked.c index 4cb33868a..a93441dd4 100644 --- a/libc/intrin/vforked.c +++ b/libc/intrin/vforked.c @@ -16,6 +16,5 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" int __vforked; diff --git a/libc/intrin/virtualprotect.c b/libc/intrin/virtualprotect.c index a71842dc5..d43cf2ab3 100644 --- a/libc/intrin/virtualprotect.c +++ b/libc/intrin/virtualprotect.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/log/libfatal.internal.h" #include "libc/nt/memory.h" diff --git a/libc/intrin/waitformultipleobjects.c b/libc/intrin/waitformultipleobjects.c index 2b6aa0be0..6a7ae4938 100644 --- a/libc/intrin/waitformultipleobjects.c +++ b/libc/intrin/waitformultipleobjects.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/synchronization.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/intrin/waitforsingleobject.c b/libc/intrin/waitforsingleobject.c index d345ef17b..bde8762ef 100644 --- a/libc/intrin/waitforsingleobject.c +++ b/libc/intrin/waitforsingleobject.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/nt/synchronization.h" #include "libc/nt/thunk/msabi.h" diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 8512da804..d6646c18b 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -21,9 +21,9 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" diff --git a/libc/log/libfatal.internal.h b/libc/log/libfatal.internal.h index eb723f5e6..6e5585e03 100644 --- a/libc/log/libfatal.internal.h +++ b/libc/log/libfatal.internal.h @@ -6,7 +6,6 @@ #include "libc/nexgen32e/bsr.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" -#include "libc/runtime/runtime.h" #include "libc/sysv/consts/nr.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 35b26d413..b83fbc8bf 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -18,8 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" diff --git a/libc/log/restoretty.greg.c b/libc/log/restoretty.greg.c index 3175a7b3c..2b43b90d7 100644 --- a/libc/log/restoretty.greg.c +++ b/libc/log/restoretty.greg.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c index 929519fd7..18da884d9 100644 --- a/libc/mem/pledge.c +++ b/libc/mem/pledge.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/filter.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" @@ -238,20 +238,20 @@ static const struct Pledges { const size_t len; } kPledgeLinux[] = { {"default", PLEDGELEN(kPledgeLinuxDefault)}, // - {"stdio", PLEDGELEN(kPledgeLinuxStdio)}, // - {"rpath", PLEDGELEN(kPledgeLinuxRpath)}, // - {"wpath", PLEDGELEN(kPledgeLinuxWpath)}, // - {"cpath", PLEDGELEN(kPledgeLinuxCpath)}, // - {"dpath", PLEDGELEN(kPledgeLinuxDpath)}, // + {"stdio", PLEDGELEN(kPledgeLinuxStdio)}, // + {"rpath", PLEDGELEN(kPledgeLinuxRpath)}, // + {"wpath", PLEDGELEN(kPledgeLinuxWpath)}, // + {"cpath", PLEDGELEN(kPledgeLinuxCpath)}, // + {"dpath", PLEDGELEN(kPledgeLinuxDpath)}, // {"tmppath", PLEDGELEN(kPledgeLinuxTmppath)}, // - {"inet", PLEDGELEN(kPledgeLinuxInet)}, // - {"fattr", PLEDGELEN(kPledgeLinuxFattr)}, // - {"unix", PLEDGELEN(kPledgeLinuxUnix)}, // - {"dns", PLEDGELEN(kPledgeLinuxDns)}, // - {"proc", PLEDGELEN(kPledgeLinuxProc)}, // - {"exec", PLEDGELEN(kPledgeLinuxExec)}, // - {"id", PLEDGELEN(kPledgeLinuxId)}, // - {0}, // + {"inet", PLEDGELEN(kPledgeLinuxInet)}, // + {"fattr", PLEDGELEN(kPledgeLinuxFattr)}, // + {"unix", PLEDGELEN(kPledgeLinuxUnix)}, // + {"dns", PLEDGELEN(kPledgeLinuxDns)}, // + {"proc", PLEDGELEN(kPledgeLinuxProc)}, // + {"exec", PLEDGELEN(kPledgeLinuxExec)}, // + {"id", PLEDGELEN(kPledgeLinuxId)}, // + {0}, // }; static const struct sock_filter kFilterStart[] = { @@ -290,7 +290,8 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { return true; } -static const uint16_t *FindPledge(const struct Pledges *p, const char *name, size_t *len) { +static const uint16_t *FindPledge(const struct Pledges *p, const char *name, + size_t *len) { int i; for (i = 0; p[i].name; ++i) { if (!strcasecmp(name, p[i].name)) { diff --git a/libc/rand/getrandom.c b/libc/rand/getrandom.c index 866dd671e..5b9f234b6 100644 --- a/libc/rand/getrandom.c +++ b/libc/rand/getrandom.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/nexgen32e/kcpuids.h" diff --git a/libc/runtime/arch_prctl.c b/libc/runtime/arch_prctl.c index f2f454430..2c958c5fb 100644 --- a/libc/runtime/arch_prctl.c +++ b/libc/runtime/arch_prctl.c @@ -19,7 +19,7 @@ #include "libc/bits/asmflag.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/nexgen32e/msr.h" #include "libc/nexgen32e/x86feature.h" diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index e15daea30..35b45eac4 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/dce.h" diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 39b464562..7ef8b4ac0 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -19,7 +19,9 @@ #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/ntspawn.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index ae9d242f3..252f5a1a3 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -19,8 +19,10 @@ #include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/nt/process.h" #include "libc/runtime/internal.h" diff --git a/libc/runtime/getinterpreterexecutablename.c b/libc/runtime/getinterpreterexecutablename.c index 898731a1f..f31ec9c7b 100644 --- a/libc/runtime/getinterpreterexecutablename.c +++ b/libc/runtime/getinterpreterexecutablename.c @@ -17,7 +17,7 @@ │ 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/macros.internal.h" #include "libc/runtime/runtime.h" diff --git a/libc/runtime/getmaxfd.c b/libc/runtime/getmaxfd.c index 81b8c06ae..59704886e 100644 --- a/libc/runtime/getmaxfd.c +++ b/libc/runtime/getmaxfd.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/rlimit.h" diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index 61634c593..8ba9cda96 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigset.h" #include "libc/dce.h" diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 32f0eade1..34f5f507f 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -22,6 +22,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/runtime/mprotect.greg.c b/libc/runtime/mprotect.greg.c index ad024e18a..5b38ba2e4 100644 --- a/libc/runtime/mprotect.greg.c +++ b/libc/runtime/mprotect.greg.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/likely.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" diff --git a/libc/runtime/mremap.c b/libc/runtime/mremap.c index 0233e962d..1ff4a3a84 100644 --- a/libc/runtime/mremap.c +++ b/libc/runtime/mremap.c @@ -20,7 +20,6 @@ #include "libc/bits/likely.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/runtime/msync-nt.c b/libc/runtime/msync-nt.c index 22e680587..d20221144 100644 --- a/libc/runtime/msync-nt.c +++ b/libc/runtime/msync-nt.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" diff --git a/libc/runtime/msync.c b/libc/runtime/msync.c index b23370cf3..4547c4e97 100644 --- a/libc/runtime/msync.c +++ b/libc/runtime/msync.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/msync.h" diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index 51c43e546..b929d9648 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -19,8 +19,9 @@ #include "libc/assert.h" #include "libc/bits/likely.h" #include "libc/bits/weaken.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" diff --git a/libc/runtime/stackuse.c b/libc/runtime/stackuse.c index dbb1cb51a..def93da70 100644 --- a/libc/runtime/stackuse.c +++ b/libc/runtime/stackuse.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/atomic.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 12e599b0d..de60e3d78 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -19,8 +19,9 @@ #include "libc/bits/bits.h" #include "libc/bits/pushpop.h" #include "libc/bits/weaken.h" -#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/elf/pf2prot.internal.h" #include "libc/errno.h" diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index 42b5d937b..e0c667bd8 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -18,21 +18,16 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/intrin/kprintf.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" -#include "libc/nt/files.h" -#include "libc/nt/struct/pollfd.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" -#include "libc/sock/yoink.inc" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/fio.h" -#include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" -#include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" -#include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize, diff --git a/libc/sock/accept4.c b/libc/sock/accept4.c index 168ca77b7..94120bfb2 100644 --- a/libc/sock/accept4.c +++ b/libc/sock/accept4.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sock/sockdebug.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/bind-nt.c b/libc/sock/bind-nt.c index 616a60220..d48c7a38a 100644 --- a/libc/sock/bind-nt.c +++ b/libc/sock/bind-nt.c @@ -17,13 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" -textwindows int sys_bind_nt(struct Fd *fd, const void *addr, uint32_t addrsize) { +textwindows int sys_bind_nt(struct Fd *fd, const void *addr, + uint32_t addrsize) { assert(fd->kind == kFdSocket); if (__sys_bind_nt(fd->handle, addr, addrsize) != -1) { return 0; diff --git a/libc/sock/bind.c b/libc/sock/bind.c index a269b21a0..0a9074278 100644 --- a/libc/sock/bind.c +++ b/libc/sock/bind.c @@ -24,6 +24,7 @@ #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sock/sockdebug.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/closesocket-nt.c b/libc/sock/closesocket-nt.c index fc7232c54..435017b23 100644 --- a/libc/sock/closesocket-nt.c +++ b/libc/sock/closesocket-nt.c @@ -21,6 +21,7 @@ #include "libc/log/backtrace.internal.h" #include "libc/mem/mem.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" /** * Closes socket on Windows. diff --git a/libc/sock/connect-nt.c b/libc/sock/connect-nt.c index 1481edfcf..bd66fe80c 100644 --- a/libc/sock/connect-nt.c +++ b/libc/sock/connect-nt.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/connect.c b/libc/sock/connect.c index 814cc18d4..71cf4c0c3 100644 --- a/libc/sock/connect.c +++ b/libc/sock/connect.c @@ -21,6 +21,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sockdebug.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c index c0e174ec4..14112e1a8 100644 --- a/libc/sock/epoll.c +++ b/libc/sock/epoll.c @@ -34,6 +34,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/spinlock.h" diff --git a/libc/sock/firewall.c b/libc/sock/firewall.c index 738eaa21a..10bdfb4b7 100644 --- a/libc/sock/firewall.c +++ b/libc/sock/firewall.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" +#include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/log/log.h" #include "libc/sock/internal.h" diff --git a/libc/sock/fixupnewsockfd.c b/libc/sock/fixupnewsockfd.c index f73bc85c6..c4116a9af 100644 --- a/libc/sock/fixupnewsockfd.c +++ b/libc/sock/fixupnewsockfd.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sock.h" diff --git a/libc/sock/gethostips.c b/libc/sock/gethostips.c index f0fa33a4a..7b47f901f 100644 --- a/libc/sock/gethostips.c +++ b/libc/sock/gethostips.c @@ -17,7 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" -#include "libc/calls/internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nt/errors.h" #include "libc/nt/iphlpapi.h" diff --git a/libc/sock/getpeername-nt.c b/libc/sock/getpeername-nt.c index e21462a78..8ed54e952 100644 --- a/libc/sock/getpeername-nt.c +++ b/libc/sock/getpeername-nt.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/getpeername.c b/libc/sock/getpeername.c index 677a2e5cf..db25832c0 100644 --- a/libc/sock/getpeername.c +++ b/libc/sock/getpeername.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sock/sockdebug.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/getsockname-nt.c b/libc/sock/getsockname-nt.c index 765c3357a..e8e1cb6e4 100644 --- a/libc/sock/getsockname-nt.c +++ b/libc/sock/getsockname-nt.c @@ -17,15 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int sys_getsockname_nt(struct Fd *fd, void *out_addr, - uint32_t *out_addrsize) { + uint32_t *out_addrsize) { assert(fd->kind == kFdSocket); if (__sys_getsockname_nt(fd->handle, out_addr, out_addrsize) != -1) { return 0; diff --git a/libc/sock/getsockname.c b/libc/sock/getsockname.c index 3def705a9..9add6709f 100644 --- a/libc/sock/getsockname.c +++ b/libc/sock/getsockname.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sock/sockdebug.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/getsockopt-nt.c b/libc/sock/getsockopt-nt.c index d64f6c5f6..41e521ed9 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -17,18 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/bits/bits.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" #include "libc/nt/struct/linger.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" -#include "libc/sock/yoink.inc" +#include "libc/sock/syscall_fd.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sol.h" -#include "libc/sysv/errfuns.h" textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, void *out_opt_optval, diff --git a/libc/sock/getsockopt.c b/libc/sock/getsockopt.c index d60d8d601..105d7ff26 100644 --- a/libc/sock/getsockopt.c +++ b/libc/sock/getsockopt.c @@ -16,13 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/inet_pton.c b/libc/sock/inet_pton.c index baa0ce034..feba1780d 100644 --- a/libc/sock/inet_pton.c +++ b/libc/sock/inet_pton.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" +#include "libc/macros.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" diff --git a/libc/sock/internal.h b/libc/sock/internal.h index 50486fb8a..29f130f11 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_SOCK_INTERNAL_H_ #define COSMOPOLITAN_LIBC_SOCK_INTERNAL_H_ -#include "libc/calls/internal.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" @@ -114,14 +113,6 @@ int32_t sys_epoll_wait(int32_t, void *, int32_t, int32_t) hidden; int sys_poll_metal(struct pollfd *, size_t, unsigned); int sys_poll_nt(struct pollfd *, uint64_t, uint64_t *) hidden; -int sys_getsockopt_nt(struct Fd *, int, int, void *, uint32_t *) hidden; -int sys_getsockname_nt(struct Fd *, void *, uint32_t *) hidden; -int sys_getpeername_nt(struct Fd *, void *, uint32_t *) hidden; -int sys_listen_nt(struct Fd *, int) hidden; -int sys_connect_nt(struct Fd *, const void *, uint32_t) hidden; -int sys_bind_nt(struct Fd *, const void *, uint32_t); -int sys_accept_nt(struct Fd *, void *, uint32_t *, int) hidden; -int sys_closesocket_nt(struct Fd *) hidden; int sys_socket_nt(int, int, int) hidden; /* int sys_socketpair_nt_stream(int, int, int, int[2]) hidden; @@ -129,18 +120,13 @@ int sys_socketpair_nt_dgram(int, int, int, int[2]) hidden; */ int sys_socketpair_nt(int, int, int, int[2]) hidden; int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden; -int sys_shutdown_nt(struct Fd *, int) hidden; -int sys_setsockopt_nt(struct Fd *, int, int, const void *, uint32_t) hidden; bool __asan_is_valid_msghdr(const struct msghdr *); ssize_t sys_send_nt(int, const struct iovec *, size_t, uint32_t) hidden; -ssize_t sys_recv_nt(struct Fd *, const struct iovec *, size_t, uint32_t) hidden; size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *, size_t) hidden; ssize_t sys_sendto_nt(int, const struct iovec *, size_t, uint32_t, void *, uint32_t) hidden; -ssize_t sys_recvfrom_nt(struct Fd *, const struct iovec *, size_t, uint32_t, - void *, uint32_t *) hidden; void WinSockInit(void) hidden; int64_t __winsockerr(void) nocallback hidden; diff --git a/libc/sock/listen-nt.c b/libc/sock/listen-nt.c index be8800b4f..1816e5223 100644 --- a/libc/sock/listen-nt.c +++ b/libc/sock/listen-nt.c @@ -20,8 +20,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" -#include "libc/sock/yoink.inc" -#include "libc/sysv/errfuns.h" +#include "libc/sock/syscall_fd.internal.h" textwindows int sys_listen_nt(struct Fd *fd, int backlog) { assert(fd->kind == kFdSocket); diff --git a/libc/sock/listen.c b/libc/sock/listen.c index c452a2ae9..a883ba7ff 100644 --- a/libc/sock/listen.c +++ b/libc/sock/listen.c @@ -21,6 +21,7 @@ #include "libc/dce.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/recv-nt.c b/libc/sock/recv-nt.c index 5c2e0bc99..46dc83ebe 100644 --- a/libc/sock/recv-nt.c +++ b/libc/sock/recv-nt.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/recv.c b/libc/sock/recv.c index c818bcd83..79993705f 100644 --- a/libc/sock/recv.c +++ b/libc/sock/recv.c @@ -22,6 +22,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index 6ce60ca11..ba229096d 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index 7dde89a4a..3ff010a06 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/dce.h" @@ -24,6 +23,7 @@ #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index 3c70b0e47..644f61886 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/select-nt.c b/libc/sock/select-nt.c index c2080efad..dfd1bd08f 100644 --- a/libc/sock/select-nt.c +++ b/libc/sock/select-nt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" #include "libc/macros.internal.h" #include "libc/sock/internal.h" diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 6cba06075..01291b628 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -19,9 +19,11 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/sendfile.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/sendfile.internal.h b/libc/sock/sendfile.internal.h new file mode 100644 index 000000000..b8b237226 --- /dev/null +++ b/libc/sock/sendfile.internal.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_SENDFILE_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_SOCK_SENDFILE_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int sys_sendfile_xnu(int32_t infd, int32_t outfd, int64_t offset, + int64_t *out_opt_sbytes, const void *opt_hdtr, + int32_t flags) hidden; + +int sys_sendfile_freebsd(int32_t infd, int32_t outfd, int64_t offset, + size_t nbytes, const void *opt_hdtr, + int64_t *out_opt_sbytes, int32_t flags) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_SENDFILE_INTERNAL_H_ */ diff --git a/libc/sock/setsockopt-nt.c b/libc/sock/setsockopt-nt.c index 4f637d8cd..3b4106875 100644 --- a/libc/sock/setsockopt-nt.c +++ b/libc/sock/setsockopt-nt.c @@ -21,6 +21,7 @@ #include "libc/macros.internal.h" #include "libc/nt/struct/linger.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/setsockopt.c b/libc/sock/setsockopt.c index c66b078eb..181f67703 100644 --- a/libc/sock/setsockopt.c +++ b/libc/sock/setsockopt.c @@ -25,6 +25,7 @@ #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/errfuns.h" diff --git a/libc/sock/shutdown-nt.c b/libc/sock/shutdown-nt.c index d6433526b..c4582d02f 100644 --- a/libc/sock/shutdown-nt.c +++ b/libc/sock/shutdown-nt.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/syscall_fd.internal.h" textwindows int sys_shutdown_nt(struct Fd *fd, int how) { if (__sys_shutdown_nt(fd->handle, how) != -1) { diff --git a/libc/sock/shutdown.c b/libc/sock/shutdown.c index 47b0ade0f..1f35fe3af 100644 --- a/libc/sock/shutdown.c +++ b/libc/sock/shutdown.c @@ -21,6 +21,7 @@ #include "libc/dce.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" /** diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index 5c069acbf..ab043b905 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nt/enum/fileflagandattributes.h" diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index 7ee42b8e9..f490916db 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -16,6 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/spinlock.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" diff --git a/libc/sock/sys_sendfile_freebsd.S b/libc/sock/sys_sendfile_freebsd.S new file mode 100644 index 000000000..286aa09b9 --- /dev/null +++ b/libc/sock/sys_sendfile_freebsd.S @@ -0,0 +1,23 @@ +/*-*- 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" + +sys_sendfile_freebsd: + jmp sys_sendfile + .endfn sys_sendfile_freebsd,globl diff --git a/libc/sock/sys_sendfile_xnu.S b/libc/sock/sys_sendfile_xnu.S new file mode 100644 index 000000000..ea1fa7400 --- /dev/null +++ b/libc/sock/sys_sendfile_xnu.S @@ -0,0 +1,23 @@ +/*-*- 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" + +sys_sendfile_xnu: + jmp sys_sendfile + .endfn sys_sendfile_xnu,globl diff --git a/libc/sock/syscall_fd.internal.h b/libc/sock/syscall_fd.internal.h new file mode 100644 index 000000000..6b5db464e --- /dev/null +++ b/libc/sock/syscall_fd.internal.h @@ -0,0 +1,24 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_ +#include "libc/calls/internal.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int sys_accept_nt(struct Fd *, void *, uint32_t *, int) hidden; +int sys_bind_nt(struct Fd *, const void *, uint32_t); +int sys_closesocket_nt(struct Fd *) hidden; +int sys_connect_nt(struct Fd *, const void *, uint32_t) hidden; +int sys_getpeername_nt(struct Fd *, void *, uint32_t *) hidden; +int sys_getsockname_nt(struct Fd *, void *, uint32_t *) hidden; +int sys_getsockopt_nt(struct Fd *, int, int, void *, uint32_t *) hidden; +int sys_listen_nt(struct Fd *, int) hidden; +int sys_setsockopt_nt(struct Fd *, int, int, const void *, uint32_t) hidden; +int sys_shutdown_nt(struct Fd *, int) hidden; +ssize_t sys_recv_nt(struct Fd *, const struct iovec *, size_t, uint32_t) hidden; + +ssize_t sys_recvfrom_nt(struct Fd *, const struct iovec *, size_t, uint32_t, + void *, uint32_t *) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_ */ diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index 16883b76d..d7ac994b3 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -16,6 +16,7 @@ │ 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/strace.internal.h" #include "libc/nt/enum/wait.h" diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index be831b5c8..364c0bf08 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -23,6 +23,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/dirent.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index cfe23c8e2..c29258e57 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -20,12 +20,12 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index b9fdfbfda..090f7e656 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -19,11 +19,11 @@ #include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.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/fmt.h" diff --git a/libc/thread/exit.c b/libc/thread/exit.c index 268514128..459b0f713 100644 --- a/libc/thread/exit.c +++ b/libc/thread/exit.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/intrin/lockxadd.h" #include "libc/runtime/runtime.h" #include "libc/thread/descriptor.h" diff --git a/libc/time/times.c b/libc/time/times.c index 2ff442c98..d31759135 100644 --- a/libc/time/times.c +++ b/libc/time/times.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/timeval.h" #include "libc/calls/struct/tms.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/nt/accounting.h" diff --git a/libc/zipos/close.c b/libc/zipos/close.c index 98fb0b3c6..cbde1e7eb 100644 --- a/libc/zipos/close.c +++ b/libc/zipos/close.c @@ -18,6 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/mem/mem.h" #include "libc/nt/runtime.h" #include "libc/zip.h" diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 7a5401e86..70fe52fe9 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -21,6 +21,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" diff --git a/libc/zipos/read.c b/libc/zipos/read.c index 2767dd10c..00d595881 100644 --- a/libc/zipos/read.c +++ b/libc/zipos/read.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/safemacros.internal.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/str/str.h" #include "libc/zip.h" diff --git a/test/libc/calls/mkntpath_test.c b/test/libc/calls/mkntpath_test.c index b051955b8..961e81ab1 100644 --- a/test/libc/calls/mkntpath_test.c +++ b/test/libc/calls/mkntpath_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index 20db7ebb4..bbb5bb866 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" diff --git a/test/libc/calls/seccomp_test.c b/test/libc/calls/seccomp_test.c index 3fe4a74b8..3ca8f0566 100644 --- a/test/libc/calls/seccomp_test.c +++ b/test/libc/calls/seccomp_test.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/filter.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/seccomp.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/errno.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" diff --git a/test/libc/calls/vfork_test.c b/test/libc/calls/vfork_test.c index 1c5bc6155..11444ba68 100644 --- a/test/libc/calls/vfork_test.c +++ b/test/libc/calls/vfork_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/state.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/calls/write_test.c b/test/libc/calls/write_test.c index 39e068529..dc443ad00 100644 --- a/test/libc/calls/write_test.c +++ b/test/libc/calls/write_test.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" diff --git a/test/libc/mem/pledge_test.c b/test/libc/mem/pledge_test.c index 808141f41..10fb53463 100644 --- a/test/libc/mem/pledge_test.c +++ b/test/libc/mem/pledge_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/mem/mem.h" diff --git a/third_party/python/Modules/posixmodule.c b/third_party/python/Modules/posixmodule.c index 2bb79f0cd..e352fd3cb 100644 --- a/third_party/python/Modules/posixmodule.c +++ b/third_party/python/Modules/posixmodule.c @@ -14,6 +14,7 @@ #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/stat.macros.h" #include "libc/calls/struct/winsize.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/calls/weirdtypes.h" #include "libc/dce.h" @@ -29,6 +30,7 @@ #include "libc/runtime/dlfcn.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/sysconf.h" +#include "libc/sock/sendfile.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/at.h" From cef50f2a6bec60be4da5cd7eb887427c32d71fda Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 23 May 2022 15:52:26 -0700 Subject: [PATCH 35/40] Write test for stack overflow detection --- libc/log/showcrashreports.c | 8 ++-- libc/rand/rand64.c | 1 - test/libc/log/backtrace_test.c | 85 ++++++++++++++++++++++++++++++++++ tool/build/lib/eztls.c | 9 ++-- 4 files changed, 96 insertions(+), 7 deletions(-) diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 7d317b07d..b7fec5f62 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -92,6 +92,7 @@ static void FreeSigAltStack(void *p) { * @see callexitontermination() */ void ShowCrashReports(void) { + char *sp; struct sigaltstack ss; /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */ @@ -105,11 +106,12 @@ void ShowCrashReports(void) { if (!IsWindows()) { bzero(&ss, sizeof(ss)); ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; + ss.ss_size = GetStackSize(); // FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here // OpenBSD sigaltstack() auto-applies MAP_STACK to the memory - if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) { + if ((sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { + ss.ss_sp = sp; if (!sigaltstack(&ss, &g_oldsigaltstack)) { __cxa_atexit(FreeSigAltStack, ss.ss_sp, 0); } else { diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index 43e7c940d..940a64a19 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -47,7 +47,6 @@ _Alignas(64) static int rand64_lock; * @note this function is not intended for cryptography * @note this function passes bigcrush and practrand * @note this function takes at minimum 15 cycles - * @asyncsignalsafe * @threadsafe * @vforksafe */ diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index 9c87717bc..a8fd805c9 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -23,6 +23,7 @@ #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/limits.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/mem/mem.h" @@ -39,6 +40,14 @@ #include "libc/x/x.h" #include "net/http/escape.h" +int StackOverflow(int f(), int n) { + if (n < INT_MAX) { + return f(f, n + 1) - 1; + } else { + return INT_MAX; + } +} + static bool OutputHasSymbol(const char *output, const char *s) { return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL")); } @@ -103,6 +112,7 @@ int (*pRodataOverrunCrash)(int) = RodataOverrunCrash; char *(*pStackOverrunCrash)(int) = StackOverrunCrash; char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash; int (*pNpeCrash)(char *) = NpeCrash; +int (*pStackOverflow)(int (*)(), int) = StackOverflow; void SetUp(void) { ShowCrashReports(); @@ -130,6 +140,8 @@ void SetUp(void) { case 8: __cxa_finalize(0); exit(pNpeCrash(0)); + case 9: + exit(pStackOverflow(pStackOverflow, 0)); default: printf("preventing fork recursion: %s\n", __argv[1]); exit(1); @@ -428,6 +440,79 @@ TEST(ShowCrashReports, testDivideByZero) { free(output); } +TEST(ShowCrashReports, testStackOverflow) { + if (IsXnu()) return; // TODO(jart): fix me + if (IsWindows()) return; // TODO(jart): fix me + if (IsFreebsd()) return; // TODO(jart): fix me + if (IsOpenbsd()) return; // TODO(jart): fix me + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "9", "--strace", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + close(fds[0]); + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + // kprintf("exit status %d\n", WEXITSTATUS(ws)); + assert(128 + SIGSEGV == WEXITSTATUS(ws) || 77 == WEXITSTATUS(ws)); + /* NULL is stopgap until we can copy symbol tablces into binary */ +#ifdef __FNO_OMIT_FRAME_POINTER__ + if (!OutputHasSymbol(output, "StackOverflow")) { + fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } +#endif + // ShowCrashReports() handled it + if (!strstr(output, gc(xasprintf("%d", pid)))) { + fprintf(stderr, "ERROR: crash report didn't have pid\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "SIGSEGV")) { + fprintf(stderr, "ERROR: crash report didn't have signal name\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!IsTiny()) { + if (!strstr(output, "Stack Overflow")) { + fprintf(stderr, "ERROR: crash report didn't have 'Stack Overflow'\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + } else { + if (!strstr(output, "SEGV_MAPERR")) { + fprintf(stderr, "ERROR: crash report didn't have 'SEGV_MAPERR'\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + } + free(output); +} + // clang-format off // // test/libc/log/backtrace_test.c:59: ubsan error: 'int' index 10 into 'char [10]' out of bounds diff --git a/tool/build/lib/eztls.c b/tool/build/lib/eztls.c index d863966a4..18093329a 100644 --- a/tool/build/lib/eztls.c +++ b/tool/build/lib/eztls.c @@ -63,8 +63,11 @@ static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) { wrote = 0; } } while (wrote); - } else if (errno != EINTR) { - return total ? total : -1; + } else { + WARNF("writev() failed %m"); + if (errno != EINTR) { + return total ? total : -1; + } } } while (i < iovlen); return total; @@ -121,6 +124,7 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) { v[1].iov_base = bio->t; v[1].iov_len = sizeof(bio->t); while ((r = readv(bio->fd, v, 2)) == -1) { + WARNF("tls read() error %s", strerror(errno)); if (errno == EINTR) { return MBEDTLS_ERR_SSL_WANT_READ; } else if (errno == EAGAIN) { @@ -128,7 +132,6 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) { } else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) { return MBEDTLS_ERR_NET_CONN_RESET; } else { - WARNF("tls read() error %s", strerror(errno)); return MBEDTLS_ERR_NET_RECV_FAILED; } } From d44ff6ce1fc96524b4a17eb900b1c6d66c2f909b Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 24 May 2022 10:19:39 -0700 Subject: [PATCH 36/40] Make improvements - Implement openpty() - Add `--assimilate` flag to APE bootloader - Restore Linux vDSO clock_gettime() support - Use `$(APE_NO_MODIFY_SELF)` on more programs --- README.md | 50 +++++- ape/ape.S | 56 +++--- examples/examples.mk | 9 + examples/greenbean.c | 35 ++-- examples/nomodifyself.c | 2 +- .../calls/__clock_gettime.S | 45 ++--- libc/calls/asan.internal.h | 19 ++ libc/calls/clock_gettime-nt.c | 1 + .../calls/clock_gettime-xnu.c | 36 ++-- libc/calls/clock_gettime.c | 83 +++++---- libc/calls/clock_gettime.h | 15 ++ libc/calls/gettimeofday.c | 2 +- libc/calls/internal.h | 1 + libc/calls/mman.greg.c | 6 +- libc/calls/nanosleep.c | 4 +- libc/calls/now.c | 11 +- libc/calls/openpty.c | 49 ++++-- libc/calls/syscall_support-sysv.internal.h | 3 +- libc/calls/utimensat.c | 4 +- libc/calls/vdsofunc.greg.c | 166 ++++++++++++------ libc/elf/checkelfaddress.c | 4 + libc/elf/scalar.h | 17 +- libc/sysv/consts.sh | 4 +- libc/sysv/errfuns.h | 13 +- test/libc/calls/clock_gettime_test.c | 32 +++- test/libc/runtime/ape_test.c | 1 + third_party/bzip2/bzip2.mk | 4 +- third_party/lua/lua.mk | 4 +- third_party/lz4cli/lz4cli.mk | 2 +- third_party/mbedtls/ecp.c | 1 + third_party/quickjs/quickjs.mk | 2 +- tool/decode/elf.c | 4 +- tool/viz/vdsosyms.c | 166 ++++++++++++++++++ 33 files changed, 600 insertions(+), 251 deletions(-) rename test/libc/calls/execve_test.c => libc/calls/__clock_gettime.S (65%) create mode 100644 libc/calls/asan.internal.h rename test/libc/time/nowl_test.c => libc/calls/clock_gettime-xnu.c (73%) create mode 100644 libc/calls/clock_gettime.h create mode 100644 tool/viz/vdsosyms.c diff --git a/README.md b/README.md index 2e8477863..2e44683bc 100644 --- a/README.md +++ b/README.md @@ -28,21 +28,57 @@ printf 'main() { printf("hello world\\n"); }\n' >hello.c gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \ -fno-omit-frame-pointer -pg -mnop-mcount \ -o hello.com.dbg hello.c -fuse-ld=bfd -Wl,-T,ape.lds \ - -include cosmopolitan.h crt.o ape.o cosmopolitan.a + -include cosmopolitan.h crt.o ape-no-modify-self.o cosmopolitan.a objcopy -S -O binary hello.com.dbg hello.com ``` -You now have a portable program. Please note that your APE binary will -assimilate itself as a conventional resident of your platform after the -first run, so it can be fast and efficient for subsequent executions. +You now have a portable program. ```sh ./hello.com -bash -c './hello.com' # zsh/fish workaround (we upstreamed patches) +bash -c './hello.com' # zsh/fish workaround (we patched them in 2021) ``` -So if you intend to copy the binary to Windows or Mac then please do -that before you run it, not after. +Since we used the `ape-no-modify-self.o` bootloader (rather than +`ape.o`) your executable will not modify itself when it's run. What +it'll instead do, is extract a 4kb program to `${TMPDIR:-/tmp}` that +maps your program into memory without needing to copy it. It's possible +to install the APE loader systemwide as follows. + +```sh +# (1) linux systems that want binfmt_misc +ape/apeinstall.sh + +# (2) for linux/freebsd/netbsd/openbsd systems +cp build/bootstrap/ape.elf /usr/bin/ape + +# (3) for mac os x systems +cp build/bootstrap/ape.macho /usr/bin/ape +``` + +If you followed steps (2) and (3) then there's going to be a slight +constant-time startup latency each time you run an APE binary. Your +system might also prevent your APE program from being installed to a +system directory as a setuid binary or a script interpreter. To solve +that, you can use the following flag to turn your binary into the +platform local format (ELF or Mach-O): + +```sh +./hello.com --assimilate +``` + +There's also some other useful flags that get baked into your binary by +default: + +```sh +./hello.com --strace +./hello.com --ftrace +``` + +If you want your `hello.com` program to be much tinier, more on the +order of 16kb rather than 60kb, then all you have to do is use + instead. See +. ### MacOS diff --git a/ape/ape.S b/ape/ape.S index c7f02a217..5074682c0 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -542,34 +542,37 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // present two choices. .ascii "o=\"$(command -v \"$0\")\"\n" // Try to use a system-wide APE loader. - .ascii "type ape >/dev/null 2>&1 && " - .ascii "exec ape \"$o\" \"$@\"\n" + .ascii "[ x\"$1\" != x--assimilate ] && " + .ascii "type ape >/dev/null 2>&1 && " + .ascii "exec ape \"$o\" \"$@\"\n" #ifdef APE_LOADER // There is no system-wide APE loader, but there is one // embedded inside the APE. So if the system is not MacOs, // extract the loader into a temp folder, and use it to // load the APE without modifying it. - .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" - .ascii "[ -x \"$t\" ] || {\n" - .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" - .shstub ape_loader_dd_skip,2 - .ascii "\" count=\"" - .shstub ape_loader_dd_count,2 - .ascii "\" bs=64 2>/dev/null\n" + .ascii "[ x\"$1\" != x--assimilate ] && {\n" + .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" + .ascii "[ -x \"$t\" ] || {\n" + .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" + .shstub ape_loader_dd_skip,2 + .ascii "\" count=\"" + .shstub ape_loader_dd_count,2 + .ascii "\" bs=64 2>/dev/null\n" #if SupportsXnu() - .ascii "[ -d /Applications ] && " - .ascii "dd if=\"$t.$$\"" - .ascii " of=\"$t.$$\"" - .ascii " skip=6" - .ascii " count=10" - .ascii " bs=64" - .ascii " conv=notrunc" - .ascii " 2>/dev/null\n" + .ascii "[ -d /Applications ] && " + .ascii "dd if=\"$t.$$\"" + .ascii " of=\"$t.$$\"" + .ascii " skip=6" + .ascii " count=10" + .ascii " bs=64" + .ascii " conv=notrunc" + .ascii " 2>/dev/null\n" #endif /* SupportsXnu() */ - .ascii "chmod 755 \"$t.$$\"\n" - .ascii "mv -f \"$t.$$\" \"$t\"\n" + .ascii "chmod 755 \"$t.$$\"\n" + .ascii "mv -f \"$t.$$\" \"$t\"\n" + .ascii "}\n" + .ascii "exec \"$t\" \"$o\" \"$@\"\n" .ascii "}\n" - .ascii "exec \"$t\" \"$o\" \"$@\"\n" #endif /* APE_LOADER */ #ifndef APE_NO_MODIFY_SELF // The default behavior is: to overwrite the header in place. @@ -590,11 +593,13 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // since only root is able to set the sticky bit, which can // be addressed simply by overriding the TMPDIR environment .ascii "t=\"${TMPDIR:-/tmp}/$0\"\n" - .ascii "[ -e \"$t\" ] || {\n" - .ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n" - .ascii "cp -f \"$o\" \"$t.$$\" &&\n" - .ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n" - .ascii "o=\"$t\"\n" + .ascii "[ x\"$1\" != x--assimilate ] || [ ! -e \"$t\" ] && {\n" + .ascii "[ x\"$1\" != x--assimilate ] && {\n" + .ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n" + .ascii "cp -f \"$o\" \"$t.$$\" &&\n" + .ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n" + .ascii "o=\"$t\"\n" + .ascii "}\n" #endif /* APE_NO_MODIFY_SELF */ .ascii "exec 7<> \"$o\" || exit 121\n" .ascii "printf '" @@ -632,6 +637,7 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .shstub ape_macho_dd_count,2 .ascii "\" conv=notrunc 2>/dev/null\n" #endif /* XNU */ + .ascii "[ x\"$1\" = x--assimilate ] && exit 0\n" #ifndef APE_NO_MODIFY_SELF .ascii "exec \"$0\" \"$@\"\n" # try to preserve argv[0] #else diff --git a/examples/examples.mk b/examples/examples.mk index 388b019b3..75002a90f 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -112,6 +112,14 @@ o/$(MODE)/examples/nomodifyself.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/examples/greenbean.com.dbg: \ + $(EXAMPLES_DEPS) \ + o/$(MODE)/examples/greenbean.o \ + o/$(MODE)/examples/examples.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + o/$(MODE)/examples/hellolua.com.dbg: \ $(EXAMPLES_DEPS) \ o/$(MODE)/examples/hellolua.o \ @@ -162,4 +170,5 @@ usr/share/dict/words: usr/share/dict/words.gz .PHONY: o/$(MODE)/examples o/$(MODE)/examples: \ o/$(MODE)/examples/package \ + o/$(MODE)/examples/pyapp \ $(EXAMPLES_BINS) diff --git a/examples/greenbean.c b/examples/greenbean.c index 275bab40b..0d475bd85 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -70,20 +70,20 @@ * Like redbean, greenbean has superior performance too, with an * advantage on benchmarks biased towards high connection counts * - * $ sudo wrk -c 300 -t 32 --latency http://10.10.10.124:8080/ + * $ wrk -c 300 -t 32 --latency http://10.10.10.124:8080/ * Running 10s test @ http://10.10.10.124:8080/ * 32 threads and 300 connections * Thread Stats Avg Stdev Max +/- Stdev - * Latency 1.07ms 8.27ms 138.55ms 98.58% - * Req/Sec 37.98k 12.61k 117.65k 80.11% + * Latency 661.06us 5.11ms 96.22ms 98.85% + * Req/Sec 42.38k 8.90k 90.47k 84.65% * Latency Distribution - * 50% 200.00us - * 75% 227.00us - * 90% 303.00us - * 99% 32.46ms - * 10033090 requests in 8.31s, 2.96GB read - * Requests/sec: 1207983.58 - * Transfer/sec: 365.19MB + * 50% 184.00us + * 75% 201.00us + * 90% 224.00us + * 99% 11.99ms + * 10221978 requests in 7.60s, 3.02GB read + * Requests/sec: 1345015.69 + * Transfer/sec: 406.62MB * */ @@ -97,6 +97,7 @@ "Referrer-Policy: origin\r\n" \ "Cache-Control: private; max-age=0\r\n" +int threads; _Atomic(int) workers; _Atomic(int) messages; _Atomic(int) listening; @@ -114,7 +115,9 @@ int Worker(void *id) { server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (server == -1) { - if (LOGGING) kprintf("%s() failed %m\n", "socket"); + kprintf("socket() failed %m\n" + " try running: sudo prlimit --pid=$$ --nofile=%d\n", + threads * 2); goto WorkerFinished; } @@ -127,8 +130,8 @@ int Worker(void *id) { errno = 0; if (bind(server, &addr, sizeof(addr)) == -1) { - if (LOGGING) kprintf("%s() failed %m\n", "socket"); - goto WorkerFinished; + kprintf("%s() failed %m\n", "socket"); + goto CloseWorker; } listen(server, 1); @@ -246,8 +249,9 @@ int Worker(void *id) { --listening; // inform the parent that this clone has finished -WorkerFinished: +CloseWorker: close(server); +WorkerFinished: --workers; return 0; } @@ -267,9 +271,9 @@ void PrintStatus(void) { } int main(int argc, char *argv[]) { + int i; char **tls; char **stack; - int i, threads; uint32_t *hostips; // ShowCrashReports(); @@ -295,7 +299,6 @@ int main(int argc, char *argv[]) { if ((1 <= threads && threads <= INT_MAX) && (tls = malloc(threads * sizeof(*tls))) && (stack = malloc(threads * sizeof(*stack)))) { - if (!threads) threads = GetCpuCount(); for (i = 0; i < threads; ++i) { if ((tls[i] = __initialize_tls(malloc(64))) && (stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, diff --git a/examples/nomodifyself.c b/examples/nomodifyself.c index 7db1e89e6..c3c5be75f 100644 --- a/examples/nomodifyself.c +++ b/examples/nomodifyself.c @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) { if (_base[0] == 'M' && _base[1] == 'Z') { printf("success: %s spawned without needing to modify its " - "executable header, thanks to APE loader\n", + "executable header", argv[0]); if (!IsWindows()) { printf(", thanks to APE loader!\n"); diff --git a/test/libc/calls/execve_test.c b/libc/calls/__clock_gettime.S similarity index 65% rename from test/libc/calls/execve_test.c rename to libc/calls/__clock_gettime.S index 2dd8ae76e..3e5d052c9 100644 --- a/test/libc/calls/execve_test.c +++ b/libc/calls/__clock_gettime.S @@ -1,5 +1,5 @@ -/*-*- 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│ +/*-*- 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 │ │ │ @@ -16,36 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dce.h" -#include "libc/log/check.h" -#include "libc/runtime/runtime.h" -#include "libc/testlib/testlib.h" +#include "libc/macros.internal.h" -void SetUp(void) { - if (getenv("_WEIRDENV")) { - for (char **e = environ; *e; ++e) { - if (!strcmp(*e, "WEIRD")) { - exit(0); - } - } - exit(7); - } -} + .initbss 201,_init___clock_gettime +__clock_gettime: + .quad 0 + .endobj __clock_gettime,globl,hidden + .previous -TEST(execve, testWeirdEnvironmentVariable) { - char *prog; - int pid, ws; - if (IsWindows()) return; - if (IsOpenbsd()) return; - prog = GetProgramExecutableName(); - ASSERT_NE(-1, (pid = fork())); - if (!pid) { - execve(prog, (char *const[]){prog, 0}, - (char *const[]){"_WEIRDENV=1", "WEIRD", 0}); - _Exit(127); - } - ASSERT_NE(-1, wait(&ws)); - EXPECT_TRUE(WIFEXITED(ws)); - EXPECT_EQ(0, WEXITSTATUS(ws)); -} + .init.start 201,_init___clock_gettime + ezlea __clock_gettime_init,ax + stosq + .init.end 201,_init___clock_gettime diff --git a/libc/calls/asan.internal.h b/libc/calls/asan.internal.h new file mode 100644 index 000000000..e73bbd39c --- /dev/null +++ b/libc/calls/asan.internal.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ +#include "libc/bits/asmflag.h" +#include "libc/calls/struct/timespec.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +forceinline bool __asan_is_valid_timespec(const struct timespec *ts) { + bool zf; + asm(ZFLAG_ASM("cmpw\t$0,0x7fff8000(%1)") + : ZFLAG_CONSTRAINT(zf) + : "r"((intptr_t)ts >> 3) + : "memory"); + return zf; +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ */ diff --git a/libc/calls/clock_gettime-nt.c b/libc/calls/clock_gettime-nt.c index e9bca40f2..50dcb25de 100644 --- a/libc/calls/clock_gettime-nt.c +++ b/libc/calls/clock_gettime-nt.c @@ -27,6 +27,7 @@ textwindows int sys_clock_gettime_nt(int clockid, struct timespec *ts) { struct timespec res; struct NtFileTime ft; static struct timespec mono; + if (!ts) return efault(); if (clockid == CLOCK_REALTIME) { GetSystemTimeAsFileTime(&ft); *ts = FileTimeToTimeSpec(ft); diff --git a/test/libc/time/nowl_test.c b/libc/calls/clock_gettime-xnu.c similarity index 73% rename from test/libc/time/nowl_test.c rename to libc/calls/clock_gettime-xnu.c index a09d77df7..3b911f9c2 100644 --- a/test/libc/time/nowl_test.c +++ b/libc/calls/clock_gettime-xnu.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 2021 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,24 +16,20 @@ │ 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/rdtscp.h" -#include "libc/sysv/consts/clock.h" -#include "libc/testlib/ezbench.h" -#include "libc/testlib/testlib.h" -#include "libc/time/time.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" -TEST(nowl, testIsMonotonic) { - long double a = nowl(); - long double b = nowl(); - EXPECT_TRUE(b > a); -} - -BENCH(nowl, bench) { - volatile int64_t c; - volatile long double x; - volatile struct timespec ts; - EZBENCH2("rdtsc", donothing, c = rdtsc()); - EZBENCH2("nowl", donothing, x = nowl()); - EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_MONOTONIC, &ts)); +int sys_clock_gettime_xnu(int clockid, struct timespec *ts) { + axdx_t ad; + ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL); + if (ad.ax != -1) { + if (ad.ax) { + ts->tv_sec = ad.ax; + ts->tv_nsec = ad.dx; + } + ts->tv_nsec *= 1000; + return 0; + } else { + return -1; + } } diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index ab086c746..8b4a962ed 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -17,6 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/asmflag.h" +#include "libc/bits/bits.h" +#include "libc/calls/asan.internal.h" +#include "libc/calls/clock_gettime.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" @@ -26,11 +30,10 @@ #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/mem/alloca.h" #include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" -static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; - /** * Returns nanosecond time. * @@ -38,6 +41,13 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; * time. Among the more popular is CLOCK_MONOTONIC. This function has a * zero syscall implementation of that on modern x86. * + * nowl l: 45𝑐 15𝑛𝑠 + * rdtsc l: 13𝑐 4𝑛𝑠 + * gettimeofday l: 44𝑐 14𝑛𝑠 + * clock_gettime l: 40𝑐 13𝑛𝑠 + * __clock_gettime l: 35𝑐 11𝑛𝑠 + * sys_clock_gettime l: 220𝑐 71𝑛𝑠 + * * @param clockid can be CLOCK_REALTIME, CLOCK_MONOTONIC, etc. * @param ts is where the result is stored * @return 0 on success, or -1 w/ errno @@ -46,54 +56,49 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; * @asyncsignalsafe */ noinstrument int clock_gettime(int clockid, struct timespec *ts) { - int rc, e; - axdx_t ad; - char buf[45]; - if (!ts) { + int rc; + char *buf; + if (IsAsan() && !__asan_is_valid_timespec(ts)) { rc = efault(); - } else if (IsAsan() && !__asan_is_valid(ts, sizeof(*ts))) { - rc = efault(); - } else if (clockid == -1) { - rc = einval(); - } else if (!IsWindows()) { - e = errno; - if ((rc = __clock_gettime(clockid, ts))) { - errno = e; - ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL); - assert(ad.ax != -1); - if (SupportsXnu() && ad.ax) { - ts->tv_sec = ad.ax; - ts->tv_nsec = ad.dx; - } - ts->tv_nsec *= 1000; - rc = 0; - } } else { - rc = sys_clock_gettime_nt(clockid, ts); + rc = __clock_gettime(clockid, ts); } +#if SYSDEBUG if (!__time_critical) { + buf = alloca(45); STRACE("clock_gettime(%d, [%s]) → %d% m", clockid, - DescribeTimespec(buf, sizeof(buf), rc, ts), rc); + DescribeTimespec(buf, 45, rc, ts), rc); } +#endif return rc; } /** - * Returns fast system clock_gettime() if it exists. + * Returns pointer to fastest clock_gettime(). */ -void *__get_clock_gettime(void) { - void *vdso; - static bool once; - static void *result; - if (!once) { - if ((vdso = __vdsofunc("__vdso_clock_gettime"))) { - __clock_gettime = result = vdso; - } - once = true; +clock_gettime_f *__get_clock_gettime(bool *opt_out_isfast) { + bool isfast; + clock_gettime_f *res; + if (IsLinux() && (res = __vdsosym("LINUX_2.6", "__vdso_clock_gettime"))) { + isfast = true; + } else if (IsXnu()) { + isfast = false; + res = sys_clock_gettime_xnu; + } else if (IsWindows()) { + isfast = true; + res = sys_clock_gettime_nt; + } else { + isfast = false; + res = sys_clock_gettime; } - return result; + if (opt_out_isfast) { + *opt_out_isfast = isfast; + } + return res; } -const void *const __clock_gettime_ctor[] initarray = { - __get_clock_gettime, -}; +hidden int __clock_gettime_init(int clockid, struct timespec *ts) { + clock_gettime_f *gettime; + __clock_gettime = gettime = __get_clock_gettime(0); + return gettime(clockid, ts); +} diff --git a/libc/calls/clock_gettime.h b/libc/calls/clock_gettime.h new file mode 100644 index 000000000..0fa6758a6 --- /dev/null +++ b/libc/calls/clock_gettime.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_ +#define COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_ +#include "libc/calls/struct/timespec.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef int clock_gettime_f(int, struct timespec *); + +extern clock_gettime_f *__clock_gettime; +hidden clock_gettime_f __clock_gettime_init; +hidden clock_gettime_f *__get_clock_gettime(bool *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_ */ diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 5698a2370..5b129ec60 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -59,7 +59,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { static textstartup void __gettimeofday_init(void) { void *vdso; - if ((vdso = __vdsofunc("__vdso_gettimeofday"))) { + if ((vdso = __vdsosym("LINUX_2.6", "__vdso_gettimeofday"))) { __gettimeofday = vdso; } } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 482ff1476..84d6cb098 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -118,6 +118,7 @@ i32 __sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; i32 __sys_wait4(i32, i32 *, i32, struct rusage *) hidden; i32 sys_clock_gettime(i32, struct timespec *) hidden; +i32 sys_clock_gettime_xnu(i32, struct timespec *) hidden; i32 sys_fstat(i32, struct stat *) hidden; i32 sys_fstatat(i32, const char *, struct stat *, i32) hidden; i32 sys_futimes(i32, const struct timeval *) hidden; diff --git a/libc/calls/mman.greg.c b/libc/calls/mman.greg.c index 89df1691a..fa0623e50 100644 --- a/libc/calls/mman.greg.c +++ b/libc/calls/mman.greg.c @@ -83,7 +83,7 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t, /** * Sorts, rounds, and filters BIOS memory map. */ -static noasan texthead void __normalize_e820(struct mman *mm) { +static noasan textreal void __normalize_e820(struct mman *mm) { uint64_t a, b; uint64_t x, y; unsigned i, j, n; @@ -113,7 +113,7 @@ static noasan texthead void __normalize_e820(struct mman *mm) { /** * Identity maps all usable physical memory to its negative address. */ -static noasan texthead void __invert_memory(struct mman *mm, uint64_t *pml4t) { +static noasan textreal void __invert_memory(struct mman *mm, uint64_t *pml4t) { uint64_t i, j, *m, p, pe; for (i = 0; i < mm->e820n; ++i) { for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size; @@ -126,7 +126,7 @@ static noasan texthead void __invert_memory(struct mman *mm, uint64_t *pml4t) { } } -noasan texthead void __setup_mman(struct mman *mm, uint64_t *pml4t) { +noasan textreal void __setup_mman(struct mman *mm, uint64_t *pml4t) { __normalize_e820(mm); __invert_memory(mm, pml4t); } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index da5f71860..4f0e6edc3 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/asan.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" @@ -31,7 +32,8 @@ noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) { int rc; char buf[2][45]; - if (!req) { + if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || + (rem && !__asan_is_valid_timespec(rem))))) { rc = efault(); } else if (req->tv_sec < 0 || !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) { diff --git a/libc/calls/now.c b/libc/calls/now.c index 3db0d9625..83012eba4 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -20,6 +20,7 @@ #include "libc/bits/initializer.internal.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/clock_gettime.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" @@ -32,10 +33,11 @@ #include "libc/sysv/consts/clock.h" #include "libc/time/time.h" +static clock_gettime_f *__gettime; + static struct Now { uint64_t k0; long double r0, cpn; - typeof(sys_clock_gettime) *clock_gettime; } g_now; static long double GetTimeSample(void) { @@ -90,7 +92,7 @@ static long double nowl_art(void) { static long double nowl_vdso(void) { long double secs; struct timespec tv; - g_now.clock_gettime(CLOCK_REALTIME, &tv); + __gettime(CLOCK_REALTIME, &tv); secs = tv.tv_nsec; secs *= 1 / 1e9L; secs += tv.tv_sec; @@ -98,9 +100,10 @@ static long double nowl_vdso(void) { } long double nowl_setup(void) { + bool isfast; uint64_t ticks; - if (0 && (g_now.clock_gettime = __get_clock_gettime())) { - // TODO(jart): Re-enable this. + __gettime = __get_clock_gettime(&isfast); + if (isfast) { nowl = nowl_vdso; } else if (X86_HAVE(INVTSC)) { RefreshTime(); diff --git a/libc/calls/openpty.c b/libc/calls/openpty.c index 2e1b282ab..080d6ef8c 100644 --- a/libc/calls/openpty.c +++ b/libc/calls/openpty.c @@ -17,33 +17,44 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/ioctl.h" #include "libc/calls/termios.h" +#include "libc/fmt/itoa.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pty.h" +#include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" /** * Opens new pseudo teletypewriter. * - * @param ilduce receives controlling tty rw fd on success - * @param aworker receives subordinate tty rw fd on success - * @param termp may be passed to tune a century of legacy behaviors - * @param winp may be passed to set terminal display dimensions + * @param mfd receives controlling tty rw fd on success + * @param sfd receives subordinate tty rw fd on success + * @param tio may be passed to tune a century of legacy behaviors + * @param wsz may be passed to set terminal display dimensions * @params flags is usually O_RDWR|O_NOCTTY - * @return file descriptor, or -1 w/ errno + * @return 0 on success, or -1 w/ errno */ -int openpty(int *ilduce, int *aworker, char *name, const struct termios *termp, - const struct winsize *winp) { - return enosys(); - /* TODO(jart) */ - /* int fd, flags; */ - /* flags = O_RDWR | O_NOCTTY; */ - /* if ((fd = posix_openpt(flags)) != -1) { */ - /* if (ioctl(m, TIOCSPTLCK, &n) || ioctl(m, TIOCGPTN, &n)) { */ - /* } else { */ - /* close(fd); */ - /* } */ - /* } else { */ - /* return -1; */ - /* } */ +int openpty(int *mfd, int *sfd, char *name, const struct termios *tio, + const struct winsize *wsz) { + int m, s, n; + char buf[20]; + if ((m = open("/dev/ptmx", O_RDWR | O_NOCTTY)) != -1) { + n = 0; + if (!ioctl(m, TIOCSPTLCK, &n) || !ioctl(m, TIOCGPTN, &n)) { + if (!name) name = buf; + name[0] = '/', name[1] = 'd', name[2] = 'e', name[3] = 'v'; + name[4] = '/', name[5] = 'p', name[6] = 't', name[7] = 's'; + name[8] = '/', FormatInt32(name + 9, n); + if ((s = open(name, O_RDWR | O_NOCTTY)) != -1) { + if (tio) ioctl(s, TCSETS, tio); + if (wsz) ioctl(s, TIOCSWINSZ, wsz); + *mfd = m; + *sfd = s; + return 0; + } + } + close(m); + } + return -1; } diff --git a/libc/calls/syscall_support-sysv.internal.h b/libc/calls/syscall_support-sysv.internal.h index f39cf80bd..3312ca2b3 100644 --- a/libc/calls/syscall_support-sysv.internal.h +++ b/libc/calls/syscall_support-sysv.internal.h @@ -12,8 +12,7 @@ int __notziposat(int, const char *); int gethostname_bsd(char *, size_t) hidden; int gethostname_linux(char *, size_t) hidden; int gethostname_nt(char *, size_t, int) hidden; -void *__get_clock_gettime(void) hidden; -void *__vdsofunc(const char *) hidden; +void *__vdsosym(const char *, const char *) hidden; void __onfork(void) hidden; void __restore_rt() hidden; void __restore_rt_netbsd(void) hidden; diff --git a/libc/calls/utimensat.c b/libc/calls/utimensat.c index d4efa6aa2..c72ec739a 100644 --- a/libc/calls/utimensat.c +++ b/libc/calls/utimensat.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" +#include "libc/calls/asan.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" @@ -39,7 +40,8 @@ int utimensat(int dirfd, const char *path, const struct timespec ts[2], int rc; char buf[12]; if (IsAsan() && (!__asan_is_valid(path, 1) || - (ts && !__asan_is_valid(ts, sizeof(struct timespec) * 2)))) { + (ts && (!__asan_is_valid_timespec(ts + 0) || + !__asan_is_valid_timespec(ts + 1))))) { rc = efault(); } else if (weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) { STRACE("zipos mkdirat not supported yet"); diff --git a/libc/calls/vdsofunc.greg.c b/libc/calls/vdsofunc.greg.c index 9e1e1fa40..b54fd40e8 100644 --- a/libc/calls/vdsofunc.greg.c +++ b/libc/calls/vdsofunc.greg.c @@ -18,76 +18,136 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" #include "libc/elf/scalar.h" #include "libc/elf/struct/ehdr.h" +#include "libc/elf/struct/phdr.h" #include "libc/elf/struct/shdr.h" #include "libc/elf/struct/sym.h" -#include "libc/log/libfatal.internal.h" +#include "libc/elf/struct/verdaux.h" +#include "libc/elf/struct/verdef.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/auxv.h" -#define LAZY_RHEL7_RELOCATION 0xfffff - -#define GetStr(tab, rva) ((char *)(tab) + (rva)) -#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset)) -#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx)) -#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name) -#define GetPhdr(e, i) \ - ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \ - (size_t)(e)->e_phentsize * (i))) -#define GetShdr(e, i) \ - ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ - (size_t)(e)->e_shentsize * (i))) - -static char *GetDynamicStringTable(Elf64_Ehdr *e, size_t *n) { - char *name; - Elf64_Half i; - Elf64_Shdr *shdr; - for (i = 0; i < e->e_shnum; ++i) { - shdr = GetShdr(e, i); - name = GetSectionName(e, GetShdr(e, i)); - if (shdr->sh_type == SHT_STRTAB) { - name = GetSectionName(e, GetShdr(e, i)); - if (name && READ64LE(name) == READ64LE(".dynstr")) { - if (n) *n = shdr->sh_size; - return GetSection(e, shdr); - } - } - } - return 0; +static inline int CompareStrings(const char *l, const char *r) { + size_t i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 255) - (r[i] & 255); } -static Elf64_Sym *GetDynamicSymbolTable(Elf64_Ehdr *e, Elf64_Xword *n) { - Elf64_Half i; - Elf64_Shdr *shdr; - for (i = e->e_shnum; i > 0; --i) { - shdr = GetShdr(e, i - 1); - if (shdr->sh_type == SHT_DYNSYM) { - if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; - if (n) *n = shdr->sh_size / shdr->sh_entsize; - return GetSection(e, shdr); +static inline int CheckDsoSymbolVersion(Elf64_Verdef *vd, int sym, + const char *name, char *strtab) { + Elf64_Verdaux *aux; + for (;; vd = (Elf64_Verdef *)((char *)vd + vd->vd_next)) { + if (!(vd->vd_flags & VER_FLG_BASE) && + (vd->vd_ndx & 0x7fff) == (sym & 0x7fff)) { + aux = (Elf64_Verdaux *)((char *)vd + vd->vd_aux); + return !CompareStrings(name, strtab + aux->vda_name); + } + if (!vd->vd_next) { + return 0; } } - return 0; } /** - * Returns Linux Kernel Virtual Dynamic Shared Object function address. + * Returns address of vDSO function. */ -void *__vdsofunc(const char *name) { - size_t m; - char *names; +void *__vdsosym(const char *version, const char *name) { + void *p; + size_t i; Elf64_Ehdr *ehdr; - Elf64_Xword i, n; - Elf64_Sym *symtab, *sym; - if ((ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR)) && - (names = GetDynamicStringTable(ehdr, &m)) && - (symtab = GetDynamicSymbolTable(ehdr, &n))) { - for (i = 0; i < n; ++i) { - if (!__strcmp(names + symtab[i].st_name, name)) { - return (char *)ehdr + (symtab[i].st_value & LAZY_RHEL7_RELOCATION); - } + Elf64_Phdr *phdr; + char *strtab = 0; + size_t *dyn, base; + unsigned long *ap; + Elf64_Sym *symtab = 0; + uint16_t *versym = 0; + Elf_Symndx *hashtab = 0; + Elf64_Verdef *verdef = 0; + + for (ehdr = 0, ap = __auxv; ap[0]; ap += 2) { + if (ap[0] == AT_SYSINFO_EHDR) { + ehdr = (void *)ap[1]; + break; } } + if (!ehdr || READ32LE(ehdr->e_ident) != READ32LE("\177ELF")) { + KERNTRACE("__vdsosym() → AT_SYSINFO_EHDR ELF not found"); + return 0; + } + + phdr = (void *)((char *)ehdr + ehdr->e_phoff); + for (base = -1, dyn = 0, i = 0; i < ehdr->e_phnum; + i++, phdr = (void *)((char *)phdr + ehdr->e_phentsize)) { + switch (phdr->p_type) { + case PT_LOAD: + // modern linux uses the base address zero, but elders + // e.g. rhel7 uses the base address 0xffffffffff700000 + base = (size_t)ehdr + phdr->p_offset - phdr->p_vaddr; + break; + case PT_DYNAMIC: + dyn = (void *)((char *)ehdr + phdr->p_offset); + break; + default: + break; + } + } + if (!dyn || base == -1) { + KERNTRACE("__vdsosym() → missing program headers"); + return 0; + } + + for (i = 0; dyn[i]; i += 2) { + p = (void *)(base + dyn[i + 1]); + switch (dyn[i]) { + case DT_STRTAB: + strtab = p; + break; + case DT_SYMTAB: + symtab = p; + break; + case DT_HASH: + hashtab = p; + break; + case DT_VERSYM: + versym = p; + break; + case DT_VERDEF: + verdef = p; + break; + } + } + if (!strtab || !symtab || !hashtab) { + KERNTRACE("__vdsosym() → tables not found"); + return 0; + } + if (!verdef) { + versym = 0; + } + + for (i = 0; i < hashtab[1]; i++) { + if (ELF64_ST_TYPE(symtab[i].st_info) != STT_FUNC && + ELF64_ST_TYPE(symtab[i].st_info) != STT_OBJECT && + ELF64_ST_TYPE(symtab[i].st_info) != STT_NOTYPE) { + continue; + } + if (ELF64_ST_BIND(symtab[i].st_info) != STB_GLOBAL) { + continue; + } + if (!symtab[i].st_shndx) { + continue; + } + if (CompareStrings(name, strtab + symtab[i].st_name)) { + continue; + } + if (versym && !CheckDsoSymbolVersion(verdef, versym[i], version, strtab)) { + continue; + } + return (void *)(base + symtab[i].st_value); + } + + KERNTRACE("__vdsosym() → symbol not found"); return 0; } diff --git a/libc/elf/checkelfaddress.c b/libc/elf/checkelfaddress.c index d42221a46..12e38520c 100644 --- a/libc/elf/checkelfaddress.c +++ b/libc/elf/checkelfaddress.c @@ -17,12 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/elf/elf.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" void CheckElfAddress(const Elf64_Ehdr *elf, size_t mapsize, intptr_t addr, size_t addrsize) { #if !(TRUSTWORTHY + ELF_TRUSTWORTHY + 0) || ELF_UNTRUSTWORTHY + 0 if (addr < (intptr_t)elf || addr + addrsize > (intptr_t)elf + mapsize) { + /* kprintf("%p-%p falls outside interval %p-%p", // */ + /* addr, addr + addrsize, // */ + /* elf, (char *)elf + mapsize); // */ abort(); } #endif diff --git a/libc/elf/scalar.h b/libc/elf/scalar.h index d7e4c7ad3..93919c8b8 100644 --- a/libc/elf/scalar.h +++ b/libc/elf/scalar.h @@ -2,15 +2,16 @@ #define COSMOPOLITAN_LIBC_ELF_SCALAR_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) -#define Elf64_Half uint16_t -#define Elf64_Word uint32_t -#define Elf64_Sword int32_t -#define Elf64_Xword uint64_t -#define Elf64_Sxword int64_t -#define Elf64_Addr uint64_t -#define Elf64_Off uint64_t +#define Elf64_Addr uint64_t +#define Elf64_Half uint16_t +#define Elf64_Off uint64_t #define Elf64_Section uint16_t -#define Elf64_Versym Elf64_Half +#define Elf64_Sword int32_t +#define Elf64_Sxword int64_t +#define Elf64_Versym Elf64_Half +#define Elf64_Word uint32_t +#define Elf64_Xword uint64_t +#define Elf_Symndx uint32_t #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_ELF_SCALAR_H_ */ diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c2bab986f..9edf6a7bb 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1323,7 +1323,6 @@ syscon termios TIOCSETD 0x5423 0x8004741b 0x8004741b 0x8004741b 0x800474 syscon termios TIOCSIG 0x40045436 0x2000745f 0x2004745f 0x8004745f 0x8004745f 0 # boop syscon termios TIOCSPGRP 0x5410 0x80047476 0x80047476 0x80047476 0x80047476 0 # boop syscon termios TIOCSTI 0x5412 0x80017472 0x80017472 0 0 0 # boop -syscon termios TIOCGPTN 0x80045430 0 0x4004740f 0 0 0 # boop syscon termios TIOCGSID 0x5429 0x40047463 0x40047463 0x40047463 0x40047463 0 # boop syscon termios TABLDISC 0 0x3 0 0x3 0x3 0 # boop syscon termios SLIPDISC 0 0x4 0x4 0x4 0x4 0 # boop @@ -1508,6 +1507,8 @@ syscon termios CSTOP 19 19 19 19 19 0 # unix consensus # Pseudoteletypewriter Control # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon pty TIOCGPTN 0x80045430 0 0x4004740f 0 0 0 # boop +syscon pty TIOCSPTLCK 0x40045431 0 0 0 0 0 # boop syscon pty TIOCPKT 0x5420 0x80047470 0x80047470 0x80047470 0x80047470 -1 # boop syscon pty TIOCPKT_DATA 0 0 0 0 0 0 # consensus syscon pty TIOCPKT_FLUSHREAD 1 1 1 1 1 1 # unix consensus @@ -1517,7 +1518,6 @@ syscon pty TIOCPKT_START 8 8 8 8 8 8 # unix consensus syscon pty TIOCPKT_NOSTOP 16 16 16 16 16 16 # unix consensus syscon pty TIOCPKT_DOSTOP 32 32 32 32 32 32 # unix consensus syscon pty TIOCPKT_IOCTL 64 64 64 64 64 64 # unix consensus -syscon pty TIOCSPTLCK 0x40045431 0 0 0 0 -1 # boop syscon pty PTMGET 0 0 0 0x40287401 0x40287401 -1 # for /dev/ptm # Modem Control diff --git a/libc/sysv/errfuns.h b/libc/sysv/errfuns.h index 5a0fc56e9..efdc02c37 100644 --- a/libc/sysv/errfuns.h +++ b/libc/sysv/errfuns.h @@ -154,11 +154,14 @@ intptr_t erfkill(void) relegated; intptr_t ehwpoison(void) relegated; #if defined(__MNO_RED_ZONE__) && defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define __ERRFUN(FUNC) \ - ({ \ - intptr_t NegOne; \ - asm("call\t" FUNC : "=a"(NegOne), "=m"(errno)); \ - NegOne; \ +#define __ERRFUN(FUNC) \ + ({ \ + intptr_t NegOne; \ + asm volatile("call\t" FUNC \ + : "=a"(NegOne) \ + : /* no outputs */ \ + : "rcx", "memory"); \ + NegOne; \ }) #define einval() __ERRFUN("einval") #define eperm() __ERRFUN("eperm") diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c index e67d61473..ce54e88de 100644 --- a/test/libc/calls/clock_gettime_test.c +++ b/test/libc/calls/clock_gettime_test.c @@ -16,14 +16,40 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/calls/clock_gettime.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/timespec.h" -#include "libc/sysv/consts/clock.h" +#include "libc/calls/struct/timeval.h" +#include "libc/calls/syscall_support-sysv.internal.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +#include "libc/time/time.h" + +TEST(clock_gettime, test) { + bool isfast; + struct timespec ts = {0}; + ASSERT_EQ(0, clock_gettime(0, &ts)); + ASSERT_NE(0, ts.tv_sec); + ASSERT_NE(0, ts.tv_nsec); + if (__is_linux_2_6_23()) { + ASSERT_GT((intptr_t)__get_clock_gettime(&isfast), + getauxval(AT_SYSINFO_EHDR)); + ASSERT_TRUE(isfast); + } +} BENCH(clock_gettime, bench) { + struct timeval tv; struct timespec ts; + gettimeofday(&tv, 0); // trigger init + clock_gettime(0, &ts); // trigger init EZBENCH2("nowl", donothing, nowl()); - EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_REALTIME, &ts)); + EZBENCH2("rdtsc", donothing, rdtsc()); + EZBENCH2("gettimeofday", donothing, gettimeofday(&tv, 0)); + EZBENCH2("clock_gettime", donothing, clock_gettime(0, &ts)); + EZBENCH2("__clock_gettime", donothing, __clock_gettime(0, &ts)); + EZBENCH2("sys_clock_gettime", donothing, sys_clock_gettime(0, &ts)); } diff --git a/test/libc/runtime/ape_test.c b/test/libc/runtime/ape_test.c index fb140b008..6a765435a 100644 --- a/test/libc/runtime/ape_test.c +++ b/test/libc/runtime/ape_test.c @@ -20,6 +20,7 @@ #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/mem/io.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/o.h" diff --git a/third_party/bzip2/bzip2.mk b/third_party/bzip2/bzip2.mk index c387dfd95..cc5e8a3ba 100644 --- a/third_party/bzip2/bzip2.mk +++ b/third_party/bzip2/bzip2.mk @@ -51,7 +51,7 @@ o/$(MODE)/third_party/bzip2/bzip2.com.dbg: \ o/$(MODE)/third_party/bzip2/bzip2.o \ o/$(MODE)/third_party/bzip2/bzip2.a.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/bzip2/bzip2recover.com.dbg: \ @@ -59,7 +59,7 @@ o/$(MODE)/third_party/bzip2/bzip2recover.com.dbg: \ o/$(MODE)/third_party/bzip2/bzip2recover.o \ o/$(MODE)/third_party/bzip2/bzip2.a.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(THIRD_PARTY_BZIP2_A_OBJS): \ diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 2005072d1..9a42729dc 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -59,7 +59,7 @@ o/$(MODE)/third_party/lua/lua.com.dbg: \ $(THIRD_PARTY_LUA_A).pkg \ o/$(MODE)/third_party/lua/lua.main.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/lua/luac.com.dbg: \ @@ -68,7 +68,7 @@ o/$(MODE)/third_party/lua/luac.com.dbg: \ $(THIRD_PARTY_LUA_A).pkg \ o/$(MODE)/third_party/lua/luac.main.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/lua/lua.com: \ diff --git a/third_party/lz4cli/lz4cli.mk b/third_party/lz4cli/lz4cli.mk index 568db66c6..2c1b93d19 100644 --- a/third_party/lz4cli/lz4cli.mk +++ b/third_party/lz4cli/lz4cli.mk @@ -56,7 +56,7 @@ o/$(MODE)/third_party/lz4cli/lz4cli.com.dbg: \ $(THIRD_PARTY_LZ4CLI_DEPS) \ $(THIRD_PARTY_LZ4CLI_OBJS) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(THIRD_PARTY_LZ4CLI_OBJS): \ diff --git a/third_party/mbedtls/ecp.c b/third_party/mbedtls/ecp.c index c60d6fc64..fc269bd5e 100644 --- a/third_party/mbedtls/ecp.c +++ b/third_party/mbedtls/ecp.c @@ -512,6 +512,7 @@ int mbedtls_ecp_check_budget( const mbedtls_ecp_group *grp, * SECP384R1 192 IANA, NIST, FRANCE, GERMANY, NSA * X25519 112-128 IANA * X448 224 IANA + * SECP256K1 128 BITCOIN * BP384R1 GERMANY * SECP521R1 FRANCE * GC512A RUSSIA diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 83c5749aa..72c17d6b4 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -145,7 +145,7 @@ o/$(MODE)/third_party/quickjs/qjs.com.dbg: \ o/$(MODE)/third_party/quickjs/repl.o \ o/$(MODE)/third_party/quickjs/qjscalc.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/quickjs/qjs.com: \ diff --git a/tool/decode/elf.c b/tool/decode/elf.c index 4a6a4b6e3..82262c124 100644 --- a/tool/decode/elf.c +++ b/tool/decode/elf.c @@ -157,7 +157,8 @@ static void printelfsectionheader(int i, char *shstrtab) { static void printelfsectionheaders(void) { Elf64_Half i; - char *shstrtab = GetElfSectionNameStringTable(elf, st->st_size); + char *shstrtab; + shstrtab = GetElfSectionNameStringTable(elf, st->st_size); if (shstrtab) { printf("\n"); printf("\t.org\t%#x\n", elf->e_shoff); @@ -341,7 +342,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "error: not an elf executable: %'s\n", path); exit(1); } - elf = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); startfile(); printelfehdr(); printelfsegmentheaders(); diff --git a/tool/viz/vdsosyms.c b/tool/viz/vdsosyms.c new file mode 100644 index 000000000..39fecbfd2 --- /dev/null +++ b/tool/viz/vdsosyms.c @@ -0,0 +1,166 @@ +/*-*- 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/strace.internal.h" +#include "libc/elf/def.h" +#include "libc/elf/scalar.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/elf/struct/phdr.h" +#include "libc/elf/struct/sym.h" +#include "libc/elf/struct/verdaux.h" +#include "libc/elf/struct/verdef.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +static inline void PrintDsoSymbolVersions(Elf64_Verdef *vd, int sym, + char *strtab) { + Elf64_Verdaux *aux; + for (;; vd = (Elf64_Verdef *)((char *)vd + vd->vd_next)) { + if (!(vd->vd_flags & VER_FLG_BASE) && + (vd->vd_ndx & 0x7fff) == (sym & 0x7fff)) { + aux = (Elf64_Verdaux *)((char *)vd + vd->vd_aux); + kprintf(" %s", strtab + aux->vda_name); + } + if (!vd->vd_next) { + break; + } + } +} + +int PrintVdsoSymbols(void) { + void *p; + size_t i; + Elf64_Ehdr *ehdr; + Elf64_Phdr *phdr; + char *strtab = 0; + size_t *dyn, base; + unsigned long *ap; + Elf64_Sym *symtab = 0; + uint16_t *versym = 0; + Elf_Symndx *hashtab = 0; + Elf64_Verdef *verdef = 0; + const char *typename, *bindname; + + for (ehdr = 0, ap = __auxv; ap[0]; ap += 2) { + if (ap[0] == AT_SYSINFO_EHDR) { + ehdr = (void *)ap[1]; + break; + } + } + if (!ehdr) { + kprintf("error: AT_SYSINFO_EHDR not found\n"); + return 1; + } + + phdr = (void *)((char *)ehdr + ehdr->e_phoff); + for (base = -1, dyn = 0, i = 0; i < ehdr->e_phnum; + i++, phdr = (void *)((char *)phdr + ehdr->e_phentsize)) { + switch (phdr->p_type) { + case PT_LOAD: + // modern linux uses the base address zero, but elders + // e.g. rhel7 uses the base address 0xffffffffff700000 + base = (size_t)ehdr + phdr->p_offset - phdr->p_vaddr; + break; + case PT_DYNAMIC: + dyn = (void *)((char *)ehdr + phdr->p_offset); + break; + default: + break; + } + } + if (!dyn || base == -1) { + kprintf("error: missing program headers\n"); + return 2; + } + + for (i = 0; dyn[i]; i += 2) { + p = (void *)(base + dyn[i + 1]); + switch (dyn[i]) { + case DT_STRTAB: + strtab = p; + break; + case DT_SYMTAB: + symtab = p; + break; + case DT_HASH: + hashtab = p; + break; + case DT_VERSYM: + versym = p; + break; + case DT_VERDEF: + verdef = p; + break; + } + } + if (!verdef) { + versym = 0; + } + + if (!strtab || !symtab || !hashtab) { + kprintf("error: strtab/symtab/hashtab not found\n"); + return 3; + } + + for (i = 0; i < hashtab[1]; i++) { + if (!symtab[i].st_shndx) { + continue; + } + + switch (ELF64_ST_BIND(symtab[i].st_info)) { + case STB_LOCAL: + bindname = "locl"; + break; + case STB_GLOBAL: + bindname = "glob"; + break; + case STB_WEAK: + bindname = "weak"; + break; + default: + bindname = "????"; + break; + } + + switch (ELF64_ST_TYPE(symtab[i].st_info)) { + case STT_FUNC: + typename = "func"; + break; + case STT_OBJECT: + typename = " obj"; + break; + case STT_NOTYPE: + typename = "none"; + break; + default: + typename = "????"; + break; + } + + kprintf("%s %s %-40s", bindname, typename, strtab + symtab[i].st_name); + PrintDsoSymbolVersions(verdef, versym[i], strtab); + kprintf("\n"); + } + + return 0; +} + +int main(int argc, char *argv[]) { + return PrintVdsoSymbols(); +} From d230a0122204042da8b66460d9c077109d3d11ea Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 25 May 2022 11:31:08 -0700 Subject: [PATCH 37/40] Make build hermetic without shell scripts - Fix some minor issues in ar.com - Have execve() look for `ape` command - Rewrite NT paths using /c/ rather /??/c:/ - Replace broken GCC symlinks with .sym files - Rewrite $PATH environment variables on startup - Make $(APE_NO_MODIFY_SELF) the default bootloader - Add all build command dependencies to build/bootstrap - Get the repository mostly building from source on non-Linux --- .gitignore | 1 - Makefile | 18 +- README.md | 82 +- ape/ape.mk | 109 +-- build/bootstrap/ar.com | Bin 61440 -> 65536 bytes build/bootstrap/cocmd.com | Bin 0 -> 40960 bytes build/bootstrap/compile.com | Bin 102400 -> 98304 bytes build/bootstrap/cp.com | Bin 0 -> 65536 bytes build/bootstrap/echo.com | Bin 0 -> 24576 bytes build/bootstrap/gzip.com | Bin 0 -> 114688 bytes build/bootstrap/make.com | Bin 0 -> 482329 bytes build/bootstrap/mkdeps.com | Bin 77824 -> 65536 bytes build/bootstrap/mkdir.com | Bin 0 -> 49152 bytes build/bootstrap/package.com | Bin 219310 -> 125835 bytes build/bootstrap/pwd.com | Bin 0 -> 24576 bytes build/bootstrap/rm.com | Bin 0 -> 32768 bytes build/bootstrap/touch.com | Bin 0 -> 32768 bytes build/bootstrap/unbundle.com | Bin 0 -> 73728 bytes build/bootstrap/zipobj.com | Bin 120564 -> 129931 bytes build/definitions.mk | 47 +- build/findtmp | 17 - build/getccversion | 39 - build/getcompile | 12 - build/hello | 20 - build/rules.mk | 10 +- examples/{shell.c => cosh.c} | 8 +- examples/cp.c | 96 --- examples/echo.c | 29 - examples/examples.mk | 35 +- examples/package/build.mk | 2 +- examples/pyapp/pyapp.mk | 2 +- examples/touch.c | 29 - libc/calls/calls.mk | 1 + libc/calls/clock_gettime.c | 4 +- libc/calls/clock_gettime.h | 2 +- libc/calls/execve-nt.greg.c | 18 +- libc/calls/execve-sysv.c | 66 +- libc/calls/getcwd-nt.c | 13 +- libc/calls/getntsyspath.S | 9 +- libc/calls/getprogramexecutablename.greg.c | 16 +- libc/calls/mkntenvblock.c | 86 +- libc/calls/mkntpath.c | 52 +- libc/calls/now.c | 2 +- libc/calls/ntaccesscheck.c | 8 +- libc/calls/ntspawn.c | 5 +- libc/calls/ntspawn.h | 3 +- libc/calls/readlinkat-nt.c | 7 +- libc/calls/strace.internal.h | 2 +- libc/calls/syscall-sysv.internal.h | 1 + libc/fmt/stoa.c | 36 +- libc/nexgen32e/gc.S | 19 +- libc/runtime/finddebugbinary.c | 33 +- libc/runtime/getdosenviron.c | 61 +- libc/x/x.h | 1 + libc/x/xfixpath.c | 63 ++ test/dsp/core/test.mk | 2 +- test/dsp/scale/test.mk | 2 +- test/dsp/tty/test.mk | 2 +- test/libc/alg/test.mk | 2 +- test/libc/bits/test.mk | 2 +- test/libc/calls/clock_gettime_test.c | 2 +- test/libc/calls/getcwd_test.c | 2 +- test/libc/calls/mkntenvblock_test.c | 15 +- test/libc/calls/readlinkat_test.c | 5 +- test/libc/calls/test.mk | 2 +- test/libc/dns/test.mk | 2 +- test/libc/fmt/fmt_test.c | 5 + test/libc/fmt/test.mk | 2 +- test/libc/intrin/test.mk | 2 +- test/libc/log/backtrace.c | 132 ++++ test/libc/log/backtrace_test.c | 162 +--- test/libc/log/test.mk | 96 ++- test/libc/mem/test.mk | 2 +- test/libc/nexgen32e/test.mk | 2 +- test/libc/rand/test.mk | 2 +- test/libc/release/metal.sh | 2 +- test/libc/release/test.mk | 26 +- test/libc/runtime/mmap_test.c | 2 + test/libc/runtime/test.mk | 4 +- test/libc/sock/test.mk | 2 +- test/libc/stdio/test.mk | 2 +- test/libc/str/test.mk | 4 +- test/libc/thread/test.mk | 2 +- test/libc/time/test.mk | 2 +- test/libc/tinymath/test.mk | 2 +- test/libc/unicode/test.mk | 2 +- test/libc/x/test.mk | 2 +- test/libc/x/xfixpath_test.c | 27 + test/libc/xed/test.mk | 2 +- test/net/http/test.mk | 2 +- test/net/https/test.mk | 2 +- test/tool/args/test.mk | 2 +- test/tool/build/lib/test.mk | 2 +- test/tool/net/test.mk | 2 +- test/tool/plinko/test.mk | 2 +- test/tool/viz/lib/test.mk | 2 +- third_party/chibicc/chibicc.c | 5 +- third_party/chibicc/chibicc.mk | 6 +- third_party/chibicc/help.txt | 9 + third_party/chibicc/test/test.mk | 4 +- third_party/gcc/bin/x86_64-linux-musl-c++ | 1 - third_party/gcc/bin/x86_64-linux-musl-c++.sym | 1 + third_party/gcc/bin/x86_64-linux-musl-cc | 1 - third_party/gcc/bin/x86_64-linux-musl-cc.sym | 1 + .../gcc/bin/x86_64-linux-musl-gcc-9.2.0 | 1 - .../gcc/bin/x86_64-linux-musl-gcc-9.2.0.sym | 1 + third_party/gcc/bin/x86_64-linux-musl-ld | 1 - third_party/gcc/bin/x86_64-linux-musl-ld.bfd | 1 - .../gcc/bin/x86_64-linux-musl-ld.bfd.sym | 1 + third_party/gcc/bin/x86_64-linux-musl-ld.sym | 1 + .../libexec/gcc/x86_64-linux-musl/9.2.0/as | 1 - .../gcc/x86_64-linux-musl/9.2.0/as.sym | 1 + third_party/gcc/unbundle.sh | 10 - third_party/gcc/x86_64-linux-musl/bin/ar | 1 - third_party/gcc/x86_64-linux-musl/bin/ar.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/as | 1 - third_party/gcc/x86_64-linux-musl/bin/as.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/ld | 1 - third_party/gcc/x86_64-linux-musl/bin/ld.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/nm | 1 - third_party/gcc/x86_64-linux-musl/bin/nm.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/objcopy | 1 - .../gcc/x86_64-linux-musl/bin/objcopy.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/objdump | 1 - .../gcc/x86_64-linux-musl/bin/objdump.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/ranlib | 1 - .../gcc/x86_64-linux-musl/bin/ranlib.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/readelf | 1 - .../gcc/x86_64-linux-musl/bin/readelf.sym | 1 + third_party/gcc/x86_64-linux-musl/bin/strip | 1 - .../gcc/x86_64-linux-musl/bin/strip.sym | 1 + third_party/mbedtls/test/test.mk | 138 ++-- third_party/python/python.mk | 746 +++++++++--------- third_party/quickjs/quickjs.mk | 4 +- third_party/smallz4/smallz4.mk | 4 +- tool/build/ar.c | 93 ++- tool/build/build.mk | 14 +- tool/build/cocmd.c | 220 ++++++ tool/build/compile.c | 65 +- tool/build/cp.c | 234 ++++++ tool/build/echo.c | 46 ++ tool/build/gzip.c | 310 ++++++++ tool/build/mkdeps.c | 17 +- tool/build/mkdir.c | 82 ++ tool/build/pwd.c | 37 + tool/build/rm.c | 103 +++ tool/build/runitd.c | 1 + tool/build/touch.c | 46 ++ tool/build/unbundle.c | 89 +++ tool/build/zipobj.c | 11 +- tool/decode/ar.c | 16 +- tool/decode/decode.mk | 2 +- tool/decode/zip.c | 1 - tool/hash/hash.mk | 2 +- tool/lambda/lambda.mk | 2 +- tool/net/help.txt | 34 +- tool/net/net.mk | 78 +- tool/net/redbean.c | 29 +- tool/plinko/plinko.mk | 4 +- tool/viz/viz.mk | 2 +- 160 files changed, 2754 insertions(+), 1342 deletions(-) create mode 100755 build/bootstrap/cocmd.com create mode 100755 build/bootstrap/cp.com create mode 100755 build/bootstrap/echo.com create mode 100755 build/bootstrap/gzip.com create mode 100755 build/bootstrap/make.com create mode 100755 build/bootstrap/mkdir.com create mode 100755 build/bootstrap/pwd.com create mode 100755 build/bootstrap/rm.com create mode 100755 build/bootstrap/touch.com create mode 100755 build/bootstrap/unbundle.com delete mode 100755 build/findtmp delete mode 100755 build/getccversion delete mode 100755 build/getcompile delete mode 100755 build/hello rename examples/{shell.c => cosh.c} (98%) delete mode 100644 examples/cp.c delete mode 100644 examples/echo.c delete mode 100644 examples/touch.c create mode 100644 libc/x/xfixpath.c create mode 100644 test/libc/log/backtrace.c create mode 100644 test/libc/x/xfixpath_test.c delete mode 120000 third_party/gcc/bin/x86_64-linux-musl-c++ create mode 100644 third_party/gcc/bin/x86_64-linux-musl-c++.sym delete mode 120000 third_party/gcc/bin/x86_64-linux-musl-cc create mode 100644 third_party/gcc/bin/x86_64-linux-musl-cc.sym delete mode 120000 third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0 create mode 100644 third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0.sym delete mode 120000 third_party/gcc/bin/x86_64-linux-musl-ld delete mode 120000 third_party/gcc/bin/x86_64-linux-musl-ld.bfd create mode 100644 third_party/gcc/bin/x86_64-linux-musl-ld.bfd.sym create mode 100644 third_party/gcc/bin/x86_64-linux-musl-ld.sym delete mode 120000 third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as create mode 100644 third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as.sym delete mode 100755 third_party/gcc/unbundle.sh delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/ar create mode 100644 third_party/gcc/x86_64-linux-musl/bin/ar.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/as create mode 100644 third_party/gcc/x86_64-linux-musl/bin/as.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/ld create mode 100644 third_party/gcc/x86_64-linux-musl/bin/ld.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/nm create mode 100644 third_party/gcc/x86_64-linux-musl/bin/nm.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/objcopy create mode 100644 third_party/gcc/x86_64-linux-musl/bin/objcopy.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/objdump create mode 100644 third_party/gcc/x86_64-linux-musl/bin/objdump.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/ranlib create mode 100644 third_party/gcc/x86_64-linux-musl/bin/ranlib.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/readelf create mode 100644 third_party/gcc/x86_64-linux-musl/bin/readelf.sym delete mode 120000 third_party/gcc/x86_64-linux-musl/bin/strip create mode 100644 third_party/gcc/x86_64-linux-musl/bin/strip.sym create mode 100644 tool/build/cocmd.c create mode 100644 tool/build/cp.c create mode 100644 tool/build/echo.c create mode 100644 tool/build/gzip.c create mode 100644 tool/build/mkdir.c create mode 100644 tool/build/pwd.c create mode 100644 tool/build/rm.c create mode 100644 tool/build/touch.c create mode 100644 tool/build/unbundle.c diff --git a/.gitignore b/.gitignore index ad6b6502a..7d5f217e9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,3 @@ __pycache__ /TAGS /bx_enh_dbg.ini /tool/emacs/*.elc -/usr/share/dict/words diff --git a/Makefile b/Makefile index f2282e120..f5e15c7a2 100644 --- a/Makefile +++ b/Makefile @@ -59,9 +59,9 @@ # # build/config.mk -SHELL = /bin/sh +SHELL = build/bootstrap/cocmd.com HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 win7 win10 xnu -SANITY := $(shell build/sanitycheck $$PPID) +#SANITY := $(shell build/sanitycheck $$PPID) .SUFFIXES: .DELETE_ON_ERROR: @@ -229,7 +229,7 @@ depend: o/$(MODE)/depend tags: TAGS HTAGS o/$(MODE)/.x: - @mkdir -p $(@D) && touch $@ + @$(COMPILE) -AMKDIR -tT$@ $(MKDIR) $(@D) o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) $(file >$@,$(SRCS)) @@ -246,11 +246,11 @@ o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS $(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x))) TAGS: o/$(MODE)/srcs-old.txt $(SRCS) - @rm -f $@ + @$(RM) $@ @$(COMPILE) -ATAGS -T$@ $(TAGS) $(TAGSFLAGS) -L $< -o $@ HTAGS: o/$(MODE)/hdrs-old.txt $(HDRS) - @rm -f $@ + @$(RM) $@ @$(COMPILE) -ATAGS -T$@ build/htags -L $< -o $@ loc: o/$(MODE)/tool/build/summy.com @@ -390,9 +390,9 @@ $(SRCS): $(HDRS): $(INCS): .DEFAULT: - @echo >&2 - @echo NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@ >&2 - @echo >&2 - rm -f o/$(MODE)/depend + @$(ECHO) >&2 + @$(ECHO) NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@ >&2 + @$(ECHO) >&2 + $(RM) o/$(MODE)/depend -include o/$(MODE)/depend diff --git a/README.md b/README.md index 2e44683bc..a00dfb8a2 100644 --- a/README.md +++ b/README.md @@ -64,15 +64,19 @@ that, you can use the following flag to turn your binary into the platform local format (ELF or Mach-O): ```sh +$ file hello.com +hello.com: DOS/MBR boot sector ./hello.com --assimilate +$ file hello.com +hello.com: ELF 64-bit LSB executable ``` There's also some other useful flags that get baked into your binary by default: ```sh -./hello.com --strace -./hello.com --ftrace +./hello.com --strace # log system calls to stderr +./hello.com --ftrace # log function calls to stderr ``` If you want your `hello.com` program to be much tinier, more on the @@ -100,22 +104,85 @@ Windows](https://justine.lol/cosmopolitan/windows-compiling.html) tutorial. It's needed because the ELF object format is what makes universal binaries possible. +Cosmopolitan officially only builds on Linux. However, one highly +experimental (and currently broken) thing you could try, is building the +entire cosmo repository from source using the cross9 toolchain. + +``` +mkdir -p o/third_party +rm -rf o/third_party/gcc +wget https://justine.lol/linux-compiler-on-windows/cross9.zip +unzip cross9.zip +mv cross9 o/third_party/gcc +build/bootstrap/make.com +``` + ## Source Builds -Cosmopolitan can be compiled from source on any Linux distro. GNU make -needs to be installed beforehand. This is a freestanding hermetic -repository that bootstraps using a vendored static gcc9 executable. -No further dependencies are required. +Cosmopolitan can be compiled from source on any Linux distro. First, you +need to download or clone the repository. ```sh wget https://justine.lol/cosmopolitan/cosmopolitan.tar.gz tar xf cosmopolitan.tar.gz # see releases page cd cosmopolitan -make -j16 +``` + +This will build the entire repository and run all the tests: + +```sh +build/bootstrap/make.com -j16 o//examples/hello.com find o -name \*.com | xargs ls -rShal | less ``` +If you get an error running make.com then it's probably because you have +WINE installed to `binfmt_misc`. You can fix that by installing the the +APE loader as an interpreter. It'll improve build performance too! + +```sh +ape/apeinstall.sh +``` + +Since the Cosmopolitan repository is very large, you might only want to +build a particular thing. Cosmopolitan's build config does a good job at +having minimal deterministic builds. For example, if you wanted to build +only hello.com then you could do that as follows: + +```sh +build/bootstrap/make.com -j16 o//examples/hello.com +``` + +Sometimes it's desirable to build a subset of targets, without having to +list out each individual one. You can do that by asking make to build a +directory name. For example, if you wanted to build only the targets and +subtargets of the chibicc package including its tests, you would say: + +```sh +build/bootstrap/make.com -j16 o//third_party/chibicc +o//third_party/chibicc/chibicc.com --help +``` + +Cosmopolitan provides a variety of build modes. For example, if you want +really tiny binaries (as small as 12kb in size) then you'd say: + +```sh +build/bootstrap/make.com -j16 MODE=tiny +``` + +Here's some other build modes you can try: + +```sh +build/bootstrap/make.com -j16 MODE=dbg # asan + ubsan + debug +build/bootstrap/make.com -j16 MODE=asan # production memory safety +build/bootstrap/make.com -j16 MODE=opt # -march=native optimizations +build/bootstrap/make.com -j16 MODE=rel # traditional release binaries +build/bootstrap/make.com -j16 MODE=optlinux # optimal linux-only performance +build/bootstrap/make.com -j16 MODE=tinylinux # tiniest linux-only 4kb binaries +``` + +For further details, see [//build/config.mk](build/config.mk). + ## GDB Here's the recommended `~/.gdbinit` config: @@ -159,4 +226,3 @@ gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000' | FreeBSD | 12 | 2018 | | OpenBSD | 6.4 | 2018 | | NetBSD | 9.1 | 2020 | -| GNU Make | 4.0 | 2015 | diff --git a/ape/ape.mk b/ape/ape.mk index 759296ec4..14b0880b3 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -15,38 +15,38 @@ PKGS += APE -APE = o/$(MODE)/ape/ape.o \ +APE = o/$(MODE)/ape/ape.o \ o/$(MODE)/ape/ape.lds -APE_NO_MODIFY_SELF = \ - o/$(MODE)/ape/ape.lds \ +APE_NO_MODIFY_SELF = \ + o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape-no-modify-self.o -APE_COPY_SELF = \ - o/$(MODE)/ape/ape.lds \ +APE_COPY_SELF = \ + o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape-copy-self.o -APELINK = \ - $(COMPILE) \ - -ALINK.ape \ - $(LINK) \ - $(LINKARGS) \ +APELINK = \ + $(COMPILE) \ + -ALINK.ape \ + $(LINK) \ + $(LINKARGS) \ $(OUTPUT_OPTION) -APE_LOADER_FLAGS = \ - -DNDEBUG \ - -iquote. \ - -Wall \ - -Wextra \ - -fpie \ - -Os \ - -ffreestanding \ - -mgeneral-regs-only \ - -mno-red-zone \ - -fno-ident \ - -fno-gnu-unique \ - -c \ - $(OUTPUT_OPTION) \ +APE_LOADER_FLAGS = \ + -DNDEBUG \ + -iquote. \ + -Wall \ + -Wextra \ + -fpie \ + -Os \ + -ffreestanding \ + -mgeneral-regs-only \ + -mno-red-zone \ + -fno-ident \ + -fno-gnu-unique \ + -c \ + $(OUTPUT_OPTION) \ $< APE_FILES := $(wildcard ape/*.*) @@ -58,32 +58,32 @@ APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S) APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o) APE_CHECKS = $(APE_HDRS:%=o/%.ok) -o/$(MODE)/ape/ape.lds: \ - ape/ape.lds \ - ape/macros.internal.h \ - libc/dce.h \ +o/$(MODE)/ape/ape.lds: \ + ape/ape.lds \ + ape/macros.internal.h \ + libc/dce.h \ libc/zip.h -o/ape/idata.inc: \ - ape/idata.internal.h \ +o/ape/idata.inc: \ + ape/idata.internal.h \ ape/relocations.h -o/$(MODE)/ape/ape-no-modify-self.o: \ - ape/ape.S \ +o/$(MODE)/ape/ape-no-modify-self.o: \ + ape/ape.S \ o/$(MODE)/ape/ape.elf - @$(COMPILE) \ - -AOBJECTIFY.S \ - $(OBJECTIFY.S) \ - $(OUTPUT_OPTION) \ - -DAPE_NO_MODIFY_SELF \ - -DAPE_LOADER="\"o/$(MODE)/ape/ape.elf\"" $< + @$(COMPILE) \ + -AOBJECTIFY.S \ + $(OBJECTIFY.S) \ + $(OUTPUT_OPTION) \ + -DAPE_NO_MODIFY_SELF \ + -DAPE_LOADER='"o/$(MODE)/ape/ape.elf"' $< -o/$(MODE)/ape/ape-copy-self.o: \ +o/$(MODE)/ape/ape-copy-self.o: \ ape/ape.S - @$(COMPILE) \ - -AOBJECTIFY.S \ - $(OBJECTIFY.S) \ - $(OUTPUT_OPTION) \ + @$(COMPILE) \ + -AOBJECTIFY.S \ + $(OBJECTIFY.S) \ + $(OUTPUT_OPTION) \ -DAPE_NO_MODIFY_SELF $< o/$(MODE)/ape/loader.o: ape/loader.c @@ -103,22 +103,23 @@ o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.macho.dbg -o/$(MODE)/ape/ape.elf.dbg: \ - o/$(MODE)/ape/loader.o \ - o/$(MODE)/ape/loader-elf.o \ +o/$(MODE)/ape/ape.elf.dbg: \ + o/$(MODE)/ape/loader.o \ + o/$(MODE)/ape/loader-elf.o \ ape/loader.lds @$(ELFLINK) -z max-page-size=0x10 -o/$(MODE)/ape/ape.macho.dbg: \ - o/$(MODE)/ape/loader-xnu.o \ - o/$(MODE)/ape/loader-macho.o \ +o/$(MODE)/ape/ape.macho.dbg: \ + o/$(MODE)/ape/loader-xnu.o \ + o/$(MODE)/ape/loader-macho.o \ ape/loader-macho.lds @$(ELFLINK) -z max-page-size=0x10 .PHONY: o/$(MODE)/ape -o/$(MODE)/ape: $(APE) \ - $(APE_CHECKS) \ - o/$(MODE)/ape/ape.elf \ - o/$(MODE)/ape/ape.macho \ - o/$(MODE)/ape/ape-copy-self.o \ +o/$(MODE)/ape: $(APE_CHECKS) \ + o/$(MODE)/ape/ape.o \ + o/$(MODE)/ape/ape.lds \ + o/$(MODE)/ape/ape.elf \ + o/$(MODE)/ape/ape.macho \ + o/$(MODE)/ape/ape-copy-self.o \ o/$(MODE)/ape/ape-no-modify-self.o diff --git a/build/bootstrap/ar.com b/build/bootstrap/ar.com index 90bd84a8f14f37a3005302dfce24311dad064f29..0f1dc30eccd2182b09cf32f9f3b2b597b9098b0d 100755 GIT binary patch literal 65536 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�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 f8aP)2=W5_w4V{B! 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 diff --git a/build/bootstrap/cocmd.com b/build/bootstrap/cocmd.com new file mode 100755 index 0000000000000000000000000000000000000000..d31f42752e92f08270aec50d4ecb91a09a146564 GIT binary patch literal 40960 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 literal 0 HcmV?d00001 diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index df9521d24e1c2f14132499325e3fb32ad9f48f1b..0ebc355711970ad0b215a55e68060485c4bdafba 100755 GIT binary patch literal 98304 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
P0F|Aw&c*8e8U@nZZ7gVI1 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 LYvBKb8u)(z8;eD{ diff --git a/build/bootstrap/cp.com b/build/bootstrap/cp.com new file mode 100755 index 0000000000000000000000000000000000000000..421917e289eaf3b144c71d66cc32f15b63442bba GIT binary patch literal 65536 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 Q^$2`D0$-27|HTOWf3V+;kN^Mx literal 0 HcmV?d00001 diff --git a/build/bootstrap/echo.com b/build/bootstrap/echo.com new file mode 100755 index 0000000000000000000000000000000000000000..7e2cb9852b5431bab7e85bb382729cd4ceb9e9ff GIT binary patch literal 24576 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 b5CjAPK|l}?1Ox#=KoAfF1OY+dKStnh%rsyO literal 0 HcmV?d00001 diff --git a/build/bootstrap/make.com b/build/bootstrap/make.com new file mode 100755 index 0000000000000000000000000000000000000000..69751182ae65aac124e82f5e11866e24a8b4f032 GIT binary patch literal 482329 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
!rfP;8k57GNH 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 U5(pDJ2zNPJ!UTf>FzM@m0UQxF*8l(j literal 219310 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 A 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

ApClWVg7_!6p0M%1apQ9nY_}>mkJ|~>J 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

u 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 diff --git a/build/bootstrap/pwd.com b/build/bootstrap/pwd.com new file mode 100755 index 0000000000000000000000000000000000000000..dc562bf5198091353b09cbd687dba42b6097ff10 GIT binary patch literal 24576 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

9w4I9cADf%xw|2t`vR>>GKes9*pVh zFHuY#(rMW{hxR?j=?K^@HB%lW?K|6zb4~WyHn@NIeV#N3dvSChf!?%Qlj@PyaGjQ) zL`%ia!lHBB9$FQS{$}g^X~I49JXOe%K9JhjSGx`!le*6Kj0Gnr;jpgxGL__1Pv^wH zk~LkaV@DDL7J>`mefJ1&(iNfBqrL=8k9?0+ygt9_xR7+IT{tqrt&jI`R;B(FUXc2T zzR-ZqxT*8^HuTT7E+60cM(m5<>Xu7wSV!V|Qz=VzZ@|Ltq(`?V-$WuO_a%JUg52LD=+uQQ<^Bia;Xca`v9Dkj?&M49Nm>TZY2a02{Bg4^ zxw#mmbL~f*a{A-+WDEHm4y5SG)q7AJE#(P{Dz!PKV_M2{(PH{R)&5S)`Pq6kOxZH* ztk6NkE(x(qy67&<(bIvCSd142n3YN!lXANC#v4ihZ?*nonG}ok$0_ zF3ny;{b0uoVI*9_H^kEhmhyurNA}R2VcGI3nu$Bc z?2qW>c9Ltr&sNyLOvJ-QvSl60KOKi(t;m)wz+)}y2pnGyrF5`oaZpR4J|GneJ;le* zWk;p6gVgi>+#k?J$|*BYT*z+DLZYwzULmfM$c%a;77>j9m{~}XlQk$|aQJ>I5qKds zKQ^;u#|(7#=c#?|W{d@$VSBU^_4el7Iw}0bq_5p7BsAZENeN8d(oEf_zIL1VRbRUU z@aaebeuMuQCtKQq1yb0Lu{Q72GpIL1=Wkr89xZ^!K1&cdW?^KczW1s7h(_Vy3+quv z3M;KNNW~9{v}Ivxk3iI}eg=!1dw)EFUT0tVDGhwk$6;`KaQ}F|QT9_%7{2bI zFli_rPiVZU10mr9 zkikXw&M+^9`OP1rViqR1O&N|m>*`C~$+=83`F;w>xB~Db1&q7`@Fxn0y8`eG z1$+bVCCXRwZz}9@f*p^A6C^dI}D-7pYdqy&vZKWXYP;v zSDIqLWS&C& zQxpAuX7J)We#l;Y4GjIZm?@+`?j3wTF};NESe?!ZhC&<+t)15)x9S`oG@k_IS)Dg4 za{=+xc>3L8rY-u7j+vr=vzZidGE{HfGdljqrq_ToO3!^xhx}gu_A->`9&-@6Z|n4L zVVV#>WTyBFn0$P1&`WJ#rU8@F<4(QySvrT$>vV?c@HG89dcq=<5+_i~H%@p8@$nOA zdlyakIld(mD9>vre1h*9{aZbO_7PIv`WkQbE!k5laoH>}ueM=rZC!13l~7)T#1;49 zvbfu#_u=?U_u-18_u)z-_u&$Yy!C6V@PF0R*3i3RVuN=QNAJ{4;_$z*RQV9%;#X#E zJy%)PAkq?=iRCr8vnSTq)i!v!bydPD4h3jfE8;4h z7x&K^xK-YI@5&9_J@w@^g12%KZhqA^;QH2{~ysw!}m z57)%>8tX1|8q2Dy^Oo1sn`5|jM@v?D8>S>CmJ%0NQaHbK3195u+$HqJ;bOPb<>b=% z5~R~OzQD;*Bz{pTDJQpJ2hsoPb;Epe9;uGqDJF9v=gih$T< z!UcJrMNW6Y0$Pf5=NEb0F6Wh{3+HqBuHrlbV*EU}$6b1dUh#Z)X@RSxWD4gl;0g*k z*J4*eDOW-$5%824e9lu?0_^$Rq7oM>z+FNV zO@!RQY{?{U32}k7V#+a(e|e?u!UBqoN=u88oWvEl7I@sa$(ZM&brw?W#L~iIATEMb z^#YSPzSvztYb;z;N=pL~1VN(!&7l-^anpVQ7kd4!Vl+OV*Qw6GTKxE-3OfcvA+H)b zN1!9P!H5(tH45JOxW1ZSURPIDbI%ewTwM6CRf_6sE4=z|zvowr4XY^1JkjU#);Ca$ z`So6}tERE4zP4s9E?|~G(`)W&poJHBg*^OMN420>TS=?XA!22Ej%DPnc-kB|_z;<}zsI;wtaD zLLV*TS`UXFVR2QxAeL975dvCAX)LL)iU^g- z$fxPZ^AJI(*AatU4tLxhiT@@Vz8DSLqv5;J@N_gZdLsDON5iaWI6oTR9Sv)u;RDfd zdo+9^8t#mS?a}aXG&~j!zmA46PFId|=+PaIVM^8?!%fLq_L~x><=*NVGMG;{kC>4$ zH~SW2Qo+2*rCi!RM1xNr-Hb>1^Wv+3LYK&is3WU+XXKcJW{Y^UqN%h!GiXJaDj2K;bFsT18^J sT$8{x30#xFH3?jkz%>b6lfX3zT$8{x30#xFH3?jkz%>c{UrFG90dRNF8vpZMAYud>%u67_44Q!+XC#CqK)eys3<&hHrkh8M z&5%J3&148k*o`;qWW8DEuH#4Icx@~m))Py}F(AMXY+@T@Y_}Nmu(5@B_WyT!W+dUT z$=>hYZ@=%}j{)_mI#qS*)TvXaPCdG_7e6|?%9c4gjxiJKVxgao87<);fkz5@MS2C> zQRo#*kOc?s+7;|0_`WilkH5iK(9n_cAY(VUVLzF1x}H^;+jcfy+xfoy_Vs?l6vmcs zy->|+SyXOrj9J~wqAov+jK|e;J9)--ez2F_Uz^KzHkcdC>gDIX*DT@yC6UFvtTVTz zb=F&$BCxsAwR2sg7|YLl)p&S7_1Nu4yNtvHN4#tNjj}g5NYR ztS=s=DC!F3tXjWrl=889c~|hpUOlU0`j=`N#J=+u{HpFpqZ9*MzUa(_X8BspadBYY z0(0A+qjhh+xIEr_VeyL>W;r%pYwji9|ApZE=SRmqIC?}}W#)+Fl;XCiy%_I2Xnf%7o)FO|kw7j&eNObYb_)(*HaZ{CxFT&r9@g=VH<13`H zGJe90QD#bWZFCiDX(N&=M}VB=9}#;jP6Q8+6=dhy9C_1H$BX4vkBT|t;!t1YriRzNASg!QUyqAar1VLg5)aiCNFUY9^POZee@sw*Nb^uuF+ zuYi_bzOJOSnoq4lp~u9L!aYUqP zQ!-|NI0*D)F~?O^msW^we)LlFlqq)S?4=Wyn(;5qjKA;wn?Ug?lktqCO-KW7;=@bL zX-K)l-@}9>N_R;6AK}6rX!KFH#!=eD5Ist}o$hFU#;7TwUYwFKD)k?#RYY$Ap_5a4 zYwp8S({I;Ye(DrL{5J{~8J~9?o?36Dp%Z9uqc_58rgbESV_s(R%(zlFR0cy|swgUV z@u}rwLSudERJjf4i2veR%13g0CT?#bRAvrJ3 zRb5?KJ&k7xam+P!LK>?qD=Ds$SUh7{?p;&aPhrY$|EVhCzvh?*e-VbiJ{pdP;lKVV z6#wO)Sk%ie-Oreg-I_b^?7==n%fy%$f9xK17V)+0H?LSYzQK3;7B`~7eQq(&nDS{~ zW%)JbWM9lkN<33uWXcMr4A3c6x~B^}4|*l1`)PNfTW)mkXN+fV^GSEE`!^acy-^(R zjt#}@QHj?rce&?qzjLQ|Z61U$LWJhs!>_s1uSwD7hUR+DDCN}E`gv?;tJ|!)&8OV8 zJSukYO+rTZzFcc|{^7*x2i@uI(gQVZVn5&2#Ogks_JKMn+Si)ot~n+4*{U*A^`Yka zic!kXuU>n=+_1Aj*m<&^8R|@iQ9BH|JM_7uS`4g3ua5F&s)gJ2g`>6`hDnxM9aF}# zmtXp#L>b9mZr8xsmAOh1d-)h5_prkFs4rG3vFznPA{xbl|8chDxhUycGk{LK)_lGB zfm*g{=Sg>KAC?>49>XY57~K#GSfXDJ1=6CQ2?hA*U7>)90+`?3wb5v#xzjD{-3ICz zUyJd~H||7*kIRD2`thW5U*hj770lJp>k`A>pB18U{s* zTC|o4^5=rzFxG~AvK_ib|K_h9wwA3~lWoz$I`etBFUL_om}sz@+wA7!cJrx@{`WEF z-w>a^!6*$uLU4xKAkQ(Tw}{^ePYLT9k_5jk#w5rwLh(YQ(;p+q?ZUd_z&Zz;glOmB zBuDxo&)bfGmxPFHPPw6Z7vw{&>-DpQ!h9Ho_GrHdL|OV#Ic=y!@KqKA;>Ce9LqKhOY63xRHkPSSn^sT}b; zx5`y9D<6W#kNPEo&vA(w+Vaq=lXC5hl{^{6*7Yx0s%6CE5_>SJZ&W`n5+~R&Iuf&l z0>mi(w6-A_fm+SsMnp3nY=k2Xw~-6c2tUI)wAhO)8OyjW_5Ym+(KV4!{+r)8(X~;e zw3YZHh9VHb$ZsWjZd^%`SVV|j_^ZMn|Wr2=x5QMk@2cU{udrUelS$;KdQ<-jvKa3%XBvcSEE}R5vlB&?~qT(hpqd_ z9_9=C*(Fs~w=m|EPuk>T!ilbtDpbT7h;Om_uEmS#)=r&Hy5F+xykx+{7968IbX8Ra zEOEaUe21bPvd-Z**QzLE^_`0q8c&ZGhJP&RT7rLa%7>OLYql=L#AjV#oo_90$luNv z0+ptGhuk5^*YmRdj~cUUI>q@mxnVDvU#EOTn-z5@g+MOm#rH4&UhpFj*rCGq3emYq z!q!iTUt#MR4UBn9>~HmLGU=qgf-IPCOLeKNx|RkBc-zuHp^pS64$$g$_|7E>fhSCY?}G92?`s;QcY{&#A&s4YIKZUk zv8s)NuL;>~(crH2^6!Jc7q&izaciWJVq^lBm1VNYS@Eq|+URhQSIC$b49n3%aaOz| zy~VT8=`Ux3-)?m36hYP}I|4O~@k~h9C$FFI!$3hM(|C5uUkLIi%G_HaVRM~KC&fAC zbAs=!XhAkPw!TRQEZFE!TLj;mNx^3dmn^d`wJx(Rx2}ND9j&bmW6=Hu@QbYI@tf9y z`T5we0pAmo1-T&EsH_5M$5!Anmbn4DH;CmsD;<|~J?u}@pF#InR`>euePA>R0d~UL%4Ea~|oh|J!~=SZ}_!Z&qgkSLw{D3>qi?B#!BXlBFMEro~e^U11 z+26i0v+KeK?%IDH`w{8-o&A7F$q@Ws^9RE6zP*7V;*Ja!b!co+GX5IfLo}8^QXtM4n>k;f@-Tx7!Xl}5@ zp0v)h&QniV7g-lAX*M7CW(;DYK~S55-ccMaA zM%xa3lpPz)DAN%9Mo4d|o+!v)3u79EF_#2gyO931hv(`Ba<;%D^;X~2dpAV;ns^&m z7jLwtACruN+88`#eOZTzw3)2^cS2y&d2|TPxo^*xZ{`cdAHw+aN)gPcQ@#M>ybvMe zYDX@_92))$gk~ay-G?nmdp(qvs$sUoUpXZNoXLHK;#0x67F%-PREox%RckG+@C5m! z`M4n8a$LD21h(jWO=;T;(lPIS2+J?{uElQnxJ921SqbTvJcD)&)LC|9m@V5gbZGGQ zfo4HH68u~Uh{=7y!U3A|0+Y-TGGc#3%y&~K4))zN?jzZ0y`fDYoUJJsvzHcI=7!iq z^>AV!(Ts6gm+a8T^a#}HPe{Nt=Li&-qXb=(u=N_5Mlc$J(I3k>P^MqmJ zMPrL?0zY0*TbzEw$H-uAa|Q-ZaHM0K#=c{S(Y}3Y98VHIFGNq;gLE{(+$QMW6X0gV z?-V& ztfs-r?QYPzbb;VcbP^{G*7<@z!GR#=bAdWGy-DoH?U_h&^xYaST?`IDH>9_VBZ0gJ z8tA*VRJyoCkT04$HUFT+a34xD>++8GAy{)KQk268ZEKL`I{hO%Q^@yAy#io9W!<44 zk{y^h1d2I0W12WX*m_;X$k|}9Znq6Zvzvn-3G&B8!5^ZKVD-+JCdFn4N`~ly^)Krp zvojo$kP8SOM7~U#C}7je;4jq&?-y!RF%ItF8$8$w#+%Q3uNf`e?l&7hj~4vV&Va`# z6hk`V3Aip|G9KRu{t0lyj1)DZ+oX#`tiRxYG>JMfR*)x69Sc$eoPo^2H%Q+zQxK<2 zgZw(DztrUP7a3(+vOZhB*f9_SuQ@M`6MWOy8#E<|4+{P`%FGE{uYL@xD<+^eQ?TO= z>YAY84GVy;E9!}vg8wm-`M5J+*asWABYv=T$3&B5>eo{2wl=f~gFk4QdPJHp_>^d; zzc>j^;RRoF5`4jq%MhbD9ZJ3xMIpbo>;F?FCnl`w`+|B1G$D;FqY%h4Ipxa^xuxT2 zkToC2XhG8l4hH2`?w^{R@~yzYJZtQ``^e9-$%>ko^bl!(LM-Mfxv8ds+r9+WD3g-8 znt#E_$d+$nv`LB9tutx5mU3%pM$=R`I2cIsp`__BZ8vq?KVjo5V6#NWw);0H>(#^q z7-;*ZW1NCQEg-!5FELP^a;M-gNH(d7|B4Jwxhp&H6ROa$H*zrGNN?btd>f27(~Vrs zegp(xgVEgS4CF@3SF+RF*SoknECvLf@7ic_f$!Q<$!^)PQD+ORXt#Q=$84Cw)d^7+ zuI_!`xwOEhc8gbyS>Ffwqk==Zx{c9+f@AH0x$WMoG3yOZ`B)Hw6ZC- z>#HrnUjq8nncU6{R3C0AS2G?Fx7GK!DGEaS4D#V>9txxS&j9KR4V6SiypQWC(>EHX zPY7es0i#Dpb2Zdc@(oI2DU1Zrex54tMN})jO_8kpxl2_Sqnn3dfw*Y^D3dnH z=zN-5R$<`?CKQZPZkrXeO16J)J8?IEZ+|dv!!#VPRDyyaHx&vs$2oZjGJ(hn5DD%V zd|-mqb{V4Fwm+aq?|DIUQlhkV8l;AK0b>CMlaw;{keWCW7R`6lgtoLA5)r0f@LmDQ zM!^reQacg|HSrc!#cG4}q3+%minVB5=0ra!0QnUh}EmyU&9Bj`{ck zbEo-yGt6g5c#ZKq|jR*I)+75&V0$gg6cxn355S{|3~4tEFqy!~k{Ay)Xk-+z|+yk6H`^X5(z_tQJJ~Z7OTKk$lt)ck#&Y$lE=d?g7GDw+VL91xivE#szT-m*tx)R+j5bm z<7o(&4yGd~PrSRRw9HjP@rue!HhxuU#kBE~r+R!@WpPniPZW5T$|O(GYS%RF^qS{$ z^XAUsmss=WEHICy=Y+B2#*JGRryVQvTAtKidD65z=1`s^e=a_{t?9KRe=aNA#8(}8 zwfg9`BY$31cH}kw$ZH#qyjI-wS~sGsvZ%yW&966)tBfnItlCsvx_XVszgF^y zr4=}6FLQ0g>3nq&UtPJ%RV{jWQL3nLZHikXidCLz_a~_^x52u!vxGKaYcE^e= zr!_x+nU>2rTZ>Gd`29Sz28v{%@C0Z6EGN?auj3n(5p zAgWTvu+nl+jYO#JRI`WAEm+~4Yqe=a`3>Xw+}SG%87>->mGGK~OF=O_f4R!s-{bL4 zTM+JDyLQ3j5jgvoV$`4c?FkYNEjaUj(;qENj+XQF{vpZa2V-(7_$#LOk_FSHhd++S{u&b*=lIOQ+dgP>BG*1 z7xEYQV_&Kp(B1jxpUk;-QI|YgyeB;2=10ZE7g+5CR_*12banlDcgr2;94NdR;MLOlz%m zv6MMx?WDS|5GXV(b)w}jEUZsKQ?{x+829iv^lLL8f05hM?me?bIB`aZJ>hMPdCn_+ zCHzu&A$W4visei88c|?}&L7jM>mwQchR$UWyFqwi*TMw~2l*^tQS1E|lETk$=Jzv8 z{5~CnD020Cy)r74g``#sPZKpP>SH)%sXhG%EpxAor&0X&jIV5CtgTsd)W6G@uh*Z= zwy>I0Pr$96A;_7|?U<0R>$Z%B%}oJGWdN1}2oFKHK8UPJ6cF{qhcXeH1_G9CpD0h< z1FK&=awZDf(m#8G!*1s1%%L7s_9G$q)e>_9y!>X^?{{%V(7~ZF!ehWlON_1qF=n0r zijJ0rH|FQhliQy(q#_Oey&l(>E(yNONr*COILo;biMXd(fO<(yunrr--fkRj+ zA5K(LulooX^gs!LWvbx2u8O=67)KMr#!;k&}uk{tn>?JI|Jo9=k(N8 zAUuOnx`l|y$d?S;pZXkP!J*pPZl4kP>vB z@(rwe9rAk)`KZzlQ$=l85k?AWyMXiidvfO~IE-bgX;C4$mBDjLUqqGVm^gl=yaQj^ zCVz>Qu~YuWDPL`#pTGa~lPDR;O3H(eZSorq7c#ci^s_7@+@c598Vc7&|14V0N|LiI za@LyKu4rmTRto-d@wYKJ<2k)JOQslR9CpRb5VBzDy_atCDwkoNDJhy{gwj0 zgFKb@8(d2;2(yNON9H@Ele4g`T!t;)=C`%RhL=+d7to%r&|(+|?)02lXgtFUu}y-U zIO<9AX0{6;Vn0A<-?HI;;4?Qrmln_n{PX34Xm+ zK#)_<)!;PSn*@!-f<9V$)YldJM48{vypXZI9XFw?NibsOQ?lVOGOCHis9eLViE9va z`ZGJ`5SWyQn84NfyK*eZHiTmGHNn@acgP0?xe?PpSHJuIT%=-&>6AaUs_#L$qNTq; z8FwsZnz=%71A^ovcN)^bfnH47kBF(8;T;;M8L{gV1kID9b2Th85!N$ntp$1aou(>a zJRR~?IJmqs@E?rbQpx#FK{9hRcJOMVo|u+Pl2f!}{RS9-`%p3X+&2Pw$TLQ!)ySmX zdawo3p3_T%oPq2E)>^%m;ToEtCO)o_IU_V)G+AqjZ(yPSb&Eiw@tVmIcr}^!Sm8VdJp|{ZdW*#ccF9(f4Bb*ItKF*mnh#sY-Q=4vc(_4SI*%s+@%(gnXiax>|Q) z6!p$HDl1Sg-DSb8$0^?uHG*_fc$#y^*p5a!!^mzQrSty z8c)XswwV}&BF|2u0}c5EZ6P>ta#aAI8>8U6x@VrZV2Iihaf~Iyq*h={U^dQQt|&i+ zkL!CPiHXDgvy-6N_hCDa+uaO>#g2-xS1~1ub+OR@b-@-ASD*n12vp!QiNDKyn(LG@b zVTvfb?_5;yoE@@Fs3)aWoEnWbP~dhhL+icQb^B?wTYVoedtjc?gOj}a@%ayBGn(c1xNJc>=3TY(F`v5!o0|R+pfc5Qo9Ec@0`E<{r6xN*Zs?k?-2Q8wPkcMoq&aYGd?xDQ3G6)nd_URkUVIM|CmN1hsWX z45pAvnl@b_ZNlm!JQoUq#hAgq!dfpr*@@MM5$pYoWGpWX)Vl$@jy9pDR`-#mCKU+T5a^Rtg`$miK)5UF=hC{R*e1u%VwS zc>^&Zx>PYyz97h7DwG!+^+Iu%@&^jyj1U=@VP1v_pQi{gH~y45Qh7QgO~Uj|jUDA~ z4P%O8Lge~3Dtg}~o$s_R7_0P0DLEy02yO#*$+eD$PRy6Av|%p4t9*mm);Bd;GG}8$ zFh;<`6ugfPGM|KALvij6;z07EvN06vt27~ht9CcW8P;=b{Re+Z`zD&W z1_E#fN}yI^GFIVYat2npm}7WkVV7SJ0-J%Zm05o_NY|esA&C+MC}D}8u%$4>89*|4!=fR1WgXhC?I5LqQEfI$qkvL_%w)>`nUb*ut0op% zD0Gmo>xWj*AuWohD35FT#5J@xl0vAoI?M}9nH0k3!r~?;Wy`0vKuWH*+k+hxixbBI z6X{IANlmI!z5~097ol4ou=z`plk7ETrHQn~BTP3uGZ!0AKTt9uz@S0-_xmt3(3}J9smHKTVJG zsK0)s@huHg_8_qJIw{hYL76{Cvc$=s=uRnr1U~pKrle?WW07*uafzm#-Z;?4Z0Cgl zlt014hKh3AoCa3FROq zNLF?e;}ANj{k=*NVvvrI8IP-SV*gB-<7dRa!6aoV=;IiiixgIdqBChLFhom%A1sWzw$P0mEUj>IuWS#B1QQX z#+>HZdhvFVM~re4t>UWX|Pbclr3d5@h9La ztJ})e*^+g^B(ON~Zi#NpLQv4zaKG4L!yh zP3oyu@8;yFnIVKUjB1V!OQ1jY0HGnlIGieJ`Gfr_vrd$ZTA^|=i}q4GwI$dY3|ohK zSaG49=Hrg^H#gj?%!fJztKGiM^BJahcdKnaH9~E|0hrV=E1-W^*B;C#Qqfh?Yg9AZ z%k6H=Sjz2gG9Huku;0HW4Ho2cHn~Ghd}+F-UOW}iGcn03x1gm4^Qj*O^pms4d?)DM z?5M;@i_CY;zPHqlI4FPvmvnu%RI&eF7!vYXR}%b0#J-`!s2%pPi5rX$GoH+((PlSyO# z58au<68btFEruD0NKA{*jKno|zCf0?kc1;L833|v&8j4*l0}uKs#2ONrKl3GN=d3@ z!ohqly38TBkr2bJYHw?P8=H;tjryu=3)S{ruCiM*dzr7ThlJ$|N)4j*8<9$pjfh}> zKzR{kfO2CuCvV?dp+xu}ThU2m#V7l^++_LIh$dgxOleRsw#85edb;3oZ0!6;HPl57 z#v?d&6^L8VFl9YdjF$DG%NW(M*zoMvL;S{w^2*}&Dj2uzUkPpTGB;4rifBL2ji z*`IPsk1Nw5M*mdBJ^+mSCw){zAoGHR^@@B@p{X0#R$%LofMMmO5MmEyLmj^jMgNo1 zPg8mzg!pG5$kaR$ivEPsF^@21B@m%asei*{t<+Ji#SC-06T2K-O$D`wEH=b+6MXEP zgLL@AL&3x>5K&S?1Sb>0PpQHoA;d@^h@pf~bP%QUu%OBf9TNPL3_$oZv(PMsE?od~ zd1aWVWgT>e;maI@MBI4OEE=yM)pBh@tCcH`orlm@7i5|g9HQ`yrxw=Pma>5@G@?A}pMtFY| z#UvN*_#6HWC@j-A1JE8SIWishfdwqt>~*PH1)mTSc?!dMltgSuVUpyweU0>1LjmxV zlgxfDmeYw31Hsi{BWDHzHvh;e0N72T{X(nn#xxNtU+v1^VRBk9z=q>6o=JUFLx1G( zWpa2jaf^5QwEB^dt0)C4yc>0Ns^4n(D>e)1r6UX{8e+?uP+&dnxjQFy)z=ei3QCi?$7JSVv9oHse~3PmL3e{@JGKv-#?=u#!pIX0dj2!?JyIGCVar zl%IeF@_9yZdu+hLlW6wNh!T%tnHdnnyf&3K4Z6Ph zqZ5wdm19C+5q9>!)=1+NQh7K;Q;MYnSE1&V6m69oa6@ECvwu#SddQo(0yc)pbJE1V zH4R&0TIY;K6y6CIJ0t~c%YK#8Y1Kzd*y>1q%!UrT1!2f3@;YP3EU2eLeg`Jf=C_*s zR^Gw==^gBh3I0thzG3S)`;UWmw%YreL#wx48 zTgbLlfmLqXi^#8JcOzfOBIK`t3~s?yj3j?PHl8NN2xFQQLp)<+)|rBj+x+?Qv_B$E z0RI9vy9w@uu8V{#HwstaZ5+||91jU&+JU+JY-^U0`a+jw3R(p@BN?*AGSnu&PgiD> z_Q5uRKkN=T=^$J&!_B8jkyBoWn)n$mV?eQjF|Ap&O-2)S3TDcX2LTKJiVmFspD~~I z-Crx~weRjX{1H)<^&Q4>XJKS7-Ea6$pap-%Niv~*l`arf8*qC1Ikwn?AGR8BPN)5X zX}&G-DqftfLKI|Fx;k{wdgZUd!TvcWAfSf{)93KGOXFXd)S{1~$(i_Seg(eblZKRD z%g*0P`JS`C-hh3XEi$+3Mqb)M1)6po{C zC0hKU@3DbQvUg_bqs5u#^VB)%=hlyfQ=a61EFPUL=q@N`4$PvaO_je;*|R0jkvDgv|<^oGI)&o`eJeu53Bi<%X|*$8eFU0l{d zJAk3}kodn@grA)=^Zki0MsF5 z@?MSIiy=841M5BfHEK5%Te+uUEZ*mS(=@!jxtsSW^)yW94FS7LG?-vBa4DqCd|qvb zOD5m)XZ{A&nA{7j3G`X ze3sx~3f9N;97JXkf#yy_yq0L_pxLqGiSwjBsC6{edi?fUXOT%qjq!9<9u*puwz_&1 zbz<;LOxCK5r|R}$5ge+HZsx$gN2*Igb-&RP4ILF!-HR7-r&8s%{~AIg;T&z zt24d9GeKB)2%BgL`9S&7`$!|GURYh|^cOTEXt(vXDMk~F+8$iuPnZGvosE5@7-a}* z2s%KhI!3uk;Y{zR@K3_G{Kfl(bzLc1X(AV$c^;-z!M+O%CPuKz!(U7#s#(zvUM ziOM(AAQ?K~-!bKRLSkWq4QOZa@$B@s)=x9HJrdCCg!CNa`Vp`vxH@~n*JSwWIbNbu z#Pfo`k4^q?hu(OBE=j)QpqD0EOi<6}pDAK1SJql;Byp!HWGA_+ zf;fhFXj2rHT-#%Bu2DBWR&#vg251)pPhssE5!SBnW`(p1_avcNp}pCVKArX_T*6|Q zv}q6y4kQdM9OU>XcQk?Pj>CA6a`jQ3!OEinkm4Yh$4kSB{(PsZQ9oS#|9i_Xu ziWWDwvcb(2;jiit^!*yQek5m#adTp$n|uEdZdS(QvK@Dz0q?--x}b39Oqe}P!FL7S zH6i^P-8SpOzN}?xnsh0MLlmNUM83)`Y!G~3UIOtpE@iOqQw~+Mp^7#J%1vC#2LNwz z3y*UPsG9Z@=T4ldIW7(1Qs!#pxfGzqzi4?wIfAFLl8>-~jd(Ez>j8->6wIYkWr(?t!+@AQsHr#j^ zWSiP3p0;8~ZcL+m2=}|OF-)j|!AdI7JDaos*Oa9WZck&gW1o5}^mFX>hJQh9QQ@kq zKYlQ7Cgue6_XrQi55mU0b9jSu_#xco6F<33jc8P`i`9gx2XlJ{4jR+m41)05nju~o zGun8DH>1qJ7=ztJ4`(FPK*vS9j-$~~N=#_S=sm4{H0CivwcS$;8T0vkAuwO()E(yb z*jZ*hQn{Tqcxp#Mnb#580vXxlQ&*00d)l}++D?2Xf1tMc+MT22-Dv}xiOQz8nAw9;3+sq>fW1(;F>b7Hf|U>l5Q(EUXL>{ zxS9cf=vdJd+ZgQYo+Ufy+-mGFiTz-c9BP9zy=B8Wno{L=JL2F0I&e)r#yzW*PJfpm zi#Eip%Q1&tgCcSZ+i3UU%q2|?r6?#|!-=jFr)jX$WnnJmn46RO1C2|-)IG<#NZD~- z+AfUg5{`8>%W(94>X!99_r_6weD-g1EKI)Ec(IT7%*=SNu^m&d@m8Dkl~EC5yR<9) z??$d|(Ds9NbEDYZ+k+f>b`{1vmINj&t=>WS5Zt)^^zcnrrHt-5b$Fc#LeW<6YCAa) zm}Uy$HlSsOe@C(4J*@@mtAvKjPGt2E-&CV@r+rI6WYmG+?%j^nS!~MBNKEjdVN3tWGk>YfUHK{qcydjqro zzfC5eX_>Wvu4kmEiEK3b2HSSi4X>0yvuB!5+5E}Bq3aSz=b_qk9pVafKut`btTdv| zsEK33#kAWGhg%JgqA>jmm%wv}{Izy<=&Ji!riz2%C%h$H5(0XzcZxe*TzD&zGPilx zu1-Ug#@u~ys6VHBQ^bLq_}-`xhqyn~>^GdAs=Xh>{n!}=bfyloCFh%gaC0v1drV^N zxjuN~(rufSf-*h%vfS-aW@b`Pq@P0!-;sWbMcy+Ze zYYA92U^xKi2MY`?SH4w$1O1^bs1}m}&-V{~9)jyMG*6_yg~LZ;TNvg7mnX0;WpcCf z5`iSV8mlg8mb>7aW1zGN#mDer2i!$}C3lg9aj*Y^fE$`L0K3S)7qpMe#=>?O&C7(7Z0R=Iz%0r2Hu3) zn5{#rhuI$;s*dEfxX!|6F+G%Pq)Cep+!gbSm`%H4)R7EvZ{`?`OeSV7(E23LpLhn_ z40r6BNjEUO0t3H8_U^91WvYzD%bRPbLYbRK(v9*=?OHk9p|?oIIK#4#0(o@JS5E$1 z3Bb6@#4jKKi0`nHj1<{Qh_k&A z+X-=^7vc&bntCDnpef3qyAiwGyRz*CR%fO;a=DoCvmH)5t`f(^*kBj5^ zJXd+;dY6ZvjqA#uxVYI)>l_WAIunVhYeKh<*SN~6DC!PH-IdkFuDCchbB&u#eW&?F8Z=7(J}bu zLC;-R?Q2`2xQTUtb}JO}h$XmetsxPtlth*@-^sG(<~VYa3Cx{ekj#oo))!TkK0IMu z3AQ%eyL1hqe^I9+|DrEN{^`3S{|q;J{)N6}Knfc9VQ_1|&hNh6HE{xCFT#v{1^5PF z0d(U%z!wp}0C)g#V>Dx95l;fl089g%2>dj_0yF*qUj^O`_*2B`Y~V$}?SPvB{}u2c z(ti)Q6!HH8{1)&{g1{dGd>ZjD0lxwyALU!n?~6slvq&Ebs7HJ(pc8T2=x4hDe+ak~ zkY0uui*yg*S-@?8H$dl?6i52+2qL}@@KvO@0X_}*9$*R5zaohEb-=$OUyK1X5Pt~p zZNwdbBJ!;UJOD_?=kZA24OoEqbAWFHz704K@Jos#{Tg5yps^2jrvV284ncY*;8%#} zQyg#=;5(>iD_|MYe+#%7_&)*u74aj0BI2I|z6f{&Fd1-A3}gW~4sa>pQa}-Oegs$o z_#c2}fcpS9gU*M5KLtDoxD8N;<;kl^PX#;(m;?AU@T&oBfLj4?0R92+S9PT)HL z2LgTz_zucpK(pP5rvTQ#IyoxVP_7#Rn?WnE__YI0Xo&yQWZ+^o=qOn zRSvU)5$G5{H}pzHcL{v=ue4aBhAZ?i1s-OtUd@-4;t|a0!e|$L%8d%Mx=D>)-{NTNUQZ$Q* zMJk_#_c_)fL1QXxo=TXln@Mn3DZyIut0d1RMn;ytS?3WwEUUWG<4FydEGw;8_q`?} zD)L3WWafa`>lM{#X>T3yFyPhhvdRst0206l6*rN&rMDGe(!*rvJ&Fz0rI0c!z-M|w z!sy{i6CFC*r7=-e1cu98Zis;zO>JZIq)M>}ycgrO1jxV6?o@F z>*-Jj_Ao<@^RQXfmFvJ%RcV!rWfzHSy7h{ODJFTBRF*^f6mmn+LU|xQsZ7+=s@heB z7!*!>(IPJ**AmpN^}z=1rHIPnVyQYLXYB)X-8z9Dhw@ccmq0~|*Flu)@q&&<$`;W| ztr}N(6{e1EL3%coud2ky8lhom1n;WQKJw_Q4rzBe^p9TKAnsir(80SR5Hxep8z`mj zQoLXjnifk~L1{T+u8mcci{(~Vij~Eccz1-pRL2$+l}WCU{!%AsGh>O%jaOVsc=~ux zm}*$&t2W_#m>x`p;KtR%s^oE1dsseN=Yl-Dha|eNqOu~@qv4^M^lndRu!eOwGOL!b z>~M3sh1SjHhH8wf(C0U4)(#WvP9}rUBhcH>9;vFTvYMVd0+eCg9-j)jY2#aJaDx(*YHqk3J zkSxmzc{%h&($yhH1d^mzdcsx+Uj*AwS-p-g^+1s~RIpxgsRERW*J#QXL6=llR#l-m zKD1Z?n@HAWm8-Z2HIXurqKgoiTij3t|475%mbYjXn$EI%3Tq{_cDO?3ZI$G9L*YEy zSnlbYg1?a)aU5xDm>YD~o%2z*_2dbQ1Y;6naKp6Rp;vg%8VSx81kydEs$F1=RM*q( zJ9_AQsE~0_dqfPE60!`vkthiPl`x?a*1lWN7}&^1!-rpkgkG~zq*>@fbnj}&$kQve zqH+UuZg)z)h!@`YoZ0yyE4BviYMG18U9}edL$%RJ7Rf+rW|>B1c=-3C36T`L@ZKMr zyP*Q|D52>Bbio~ojtme?wWbK!;aAu|n371P*C1C&i~hU$5Yp7F+$tBTh*+eJt0&k~ zDez91aWwg!^qj;BfyvUI|E@i!F?_j@<+7)cny2OYlc5Tjcl0ZPIikOWXPbTvo`bd2 zpR-z|PGlxZWjw;eH2hn7(-^EX^tHgZYtJ9CXAu4(nx0?klJFdL&uKh$jGlFR!gTdX z0)@|`+YuhF(K*KmV__?Sv1*vf8iz;qQ-Oa$ zweEQ>|7X!BL2ZbZ`l5#UqxS4z#Pe}|6;h9B^dDx_(i3{hKZkXVV(fmc)F$RdPF@S! zwA!a@9KNK{8KB`uXwRXeNzT$}qWr+tc(H#X-;;# zur%}VYcN`2AXme3z~GIg0ka;jSYoPMQv|1LjVlh;Xg#b{cLSi%9Edn+WFbw(3-P|&(F1IIjqjHU^ma6RWKIiBO&C#$}`_SZ$4-tPn^w~ zjgJ}T^WmbA=t2Qod2>-?9$J+@-&sK1&(52h%{%AjBlm26e!d+Q;7%Ee#zAgiHh(O? zP_TowJmT1j|5*i&xjB>@l@{b7I+o9|&v82N`fQe+>YPit;|k{HA>({VRVy%-x8^zW zsYWc3sWdWzAZX;EIYdztH}wm+(CWA6q4C*Pjq2>%#XpY4t#*l6(DNzygl6i)V|r=6 zIeqGsvAj7w{bB7nkuQKv%yX@VH|3#ukxbFj6&U(_Yjm*KIgQ z$!$3c){tYPA*uYGtBT0TDu!)`$G%bHE-bqqd=Ivws4N$beGWeCJZn=(JYlY#MIOHeQ3;xIT4zw?ty7zheprN$e4bg zt-o&IU!sgpdiA$es_z?eoKaDJ-@^W1 z>P9Ddjb7btwe>Wy(6sROCeHu+Ox#c8*ykg)Qr-GqeX9SjoG;@I4LOZD^*R5Q z^IA?H_PmaHb#LYz&S}XB8eh*D{#3u$bzeq*qHE7NhSBwE&hDIr9~lqj_@lOR`c7kQ z&e5EvoP#;dIe&?2&3QRCs!_-KVS_DawC=vPoamqH*q)p>ay~YmG`@k-@pU=nY=6$) zoDuKmu&d+V$~lm8?;iboIqdD6zvS%Jy_0i~I*k5>`UN}XVG(+D-4a@ET6CL)7B9V{ zgqB1kw1!mAT~G=MmE1`}F0Eg-dcCII_&eaWgu16^uljDN_ZMRCO5?6HXo|S22X|#~ zR}Z#n-qPK%aCa=+9Se8ILgefD|KGL;+%W9=j$yXFzni(eA)ndSWNEUfm!9=@TgAaj5)->q zXK73C+-GGJftfFLpY0mUFogv#n+^@Gov`JI_sneXr%mn6?ahxJx})DHOXs$hMsw27 zF8_(1r$_zzxnDl_tLKWJ{nfJIQtw5tr3tw%hnn*jJa(wPt-a0EY&p48_j1gk!Apv3 zNB+FU*TQXUx!yFR--x8T0XucqTV~wDCDrNEJNaNfX_2pX-~%{tFiUTjNj0Dn@3-lFz;l%H8$H z#KeL{mbMr5x;LI*k>LG$>GNODact}k_7U&@!t26k$Hm_{E+xJ?J0&%(w7Rmgq^gXY zSjVNLrl*WgiC@NTNJ+J%aHF%i4HGApcs%8mp&9^xUW=xzcR@OW`8N?~y#0{Vz zrl8b^9^xL1FDv89-AD|=)da;!si`TPXI*(sb_%CG)9*>)N~@(RkkrDKDH$o;Do^%3 zQ^8;lvqZU!o6OhLRFs#Nh~?E)o^ZvY1%wIzu#9^mq%G+YlBLyEb=g(bVy#qFdISB^ zHI>z6-1KSFh<#!*zOs&+=#GZ8O2t1MugOZQV6U(qzL_}CD1Wa>Ae?2~Xl|k_DlN3b zgMTlB7GJrpth|<+Sc6Otieo1K=;4$b>rfNsSZl?wK*o-}Nj<1(TpP+o&N3xkQztPc zTD9p}vp^gKdXreibCbWj2}hJ}68BHxA{=P+QMSfW`qVHzio22SIBwS1>ETwKo;7yjKUAxz-U31= zGx}=o-5HrTYA%;Coe=+nf<^o1O}nSo8tG^R>fC6Jh?;2)iSAgCojNUk6ywZJ#rDb^A_zk9It}FD;E(n_j%_#J2WrP1{awQ(k2l6JxiWwp-5GEuD6YYB^~+ zw@_$pWsIQ0eY^(^$tYi&7-r3*AoTu(RG}e0&I;}rMm;4UJDwVb5jGuQm>8zj0D;A7 zNO&k0MnREcmaJt2`3u2sd`Lh#*$&;Je+$=+TFb0ySYXxOb(V8*Uyh-CFwtbUwAn4k z?3R-qgN8GVe|fm8c)$n-v>>JCrZYEclvt@J|jI(#aqMhM;c(#!=MME zk+g>(VD60RA2X1R zh6x@T7Y$j%FT^PRw7TIn3bm3WmC!RB*oXw0ZXg$?5&4az&_WwZ87A$9*#Cb5M7RC# zkeVdAHi#5|KmLfJD8y^@xgRYzsjNgSA;fn4)!>iP672Idp)(%oK6f25*Xe<$mL;8g z952)^^bEtFfWMV1^Z9x9rS^jPgzfuqeFOjFJJER@e=AoO*^6(OcUZ%De!yFlSJDUK zDay~vl~DnNv;7S{vo#!Xe;81`F(UOoq+ER-;!DBb%9S?0i0>2e!+sOx5A^6EzM?Xt z@XD1@fm7)3(2L}!`rW`+I9t!}%9X1;o}SQ6=y|Q-SZ{BlzVxEMumzv0|_m>uRB4^2~YpbCMAQTWGv; z*A-P2Fva~w@Ez1UWSzrrX;6`e_nnOsnoms@Mt>~mT0$>4<%7%a3-XIG@bQcIg?y1i z{&t}ds5TcmF)ZLzWux^n< zK4y~-%FXxs9~#`H9uzVk8f^BAyf-j)uvWIicQ#oFJYp7nUz;xdslG{iCln(e)Yu7# zgUwnRUTqe9`;pA12Y0QP{uKI?u<1ecTNCvZ6C?NUfXl<3Le1qw(yNzZB$8l=;`g!e%=e zofPks&kDXb^nz@5YeI_(eQg z{2E`hun-G2;CpJSAQz>YlvN<@*rYy%&dvIs0-wA;*4L#I`)AXT&(>2X;{EdszEd$k z3&Y@IofdpwTt{)SxtXie>fHF{>XHQi@>G*UzY-Pt+>!aQlbv%ZG}I}dS7I)!SndIr zkd+_@Elro6b@&&X1^?94NEOdJb)Pxldm)YDx`vwWj%uSY`b{A&B*@939S&b>Mn@r( zG&fUXa^-Jw*_>R!Tt@I6F!!l6yzXGHy5H=L9c5#fM=|+~(dpt)JMD>KW`)NP1E+

7WbRV+{kQ$N!3i z{}G53uklN^n>H*g$ai7T!Mvzhqg^a?G1Ggb|H`xBS&!|b; zP#`R$ZL1;1js<4qX$oBwGFxh=3i3C?_-0}J1wq#?WPan}*v3TG7PxmG@4GT=z23K< zv$2f{CO-41WD?Zo&`Ex$4g+bBto?UFVA?q}2#vXKEtIbn3Z);w_;X4L%&1fT8pe4s z-jJ#tsSt8#_%HD`8*f;BcocDe4aZH?Fpnl&J}Cs8sr`l0lcD$)TWbG_6il$Fd;`t! z1o?#Jm>^$wT)rR#9@Y8wr*A3B#JKkXEWhCEj$8k6iy;%T5;8A%hHOpLS+{0ctXr~l zsPL9Vi=ZA3eIW$I)c#=MRT}dG(<~4&LVrQXcTFb_^<6XVA=zoIp$#CcZGR|scNUPA zrnrOq;KV?pS(CIT*=mUG6{yocU;u_WN1(_OBk1-Eo4Uz73W22a7$e!AJx=_?5n;U# zO&K*=G_}~KaFYeK#pySGj0Bc8XCQHkBNNLs_N^mK_ASffIgxbN0G)iRcM?fxhd@rSr=K`Mjl5^AB2#!;zceS9H7w!CE>Iqa4E9<|b*r(?6y&jeNg}mjNs% z`K^W#xq+!8pqN9mW{87@P2a2NIqQx57TZWvI~e*%kUu60{xF3Bym!_NDK0lqHbNKL zw^J7#o#Bv#T|oFC@+Hzl0gGNnf4L!ayHKx+@o)!U~j z2vedVex1`_Zg%=hOtLN2kSm|>NQA)a&qOs(iG;&NrAjj;KFFE9v zjweCZatyr%RZkoW%B}3b?03r71BnHE+&g>7&$7vinv{GOY5#yYj8pRd`X+YEGFYQ* zie~G7gPxHqUqf$`lK4%tX}Fg18)!t+R5vsfNb;einJ{hFbnL%i;VWRX>f_q|52YH^ zq*u|=_RK^-1%(Dcc=cz{QJr$9;4exwt4Y5_0;k-S8~7O&=-3_I7;t1Zv5&n4Mx2=@ zwtg>O1YeWM(&-H3>*dS2neBBhwh@y7LFelpCob}JFPH4rts8W1?AA zV`UrP^PNo(Y;3oB)!4dzNFNg#$u@4#2a1li2j;hXuf*0Fo$}ET1Sc2*MenuqTN0-W z@b%S}(60e~>TGsf7K)Ell&znPfSdO{Y>t7@K8JkR`nw{iL9>ARQbQ$E7VpCbN_0`f z^b2E*I$#WFXtw@aG@qUQtCW$u?%j-B{X)(GQv@lj9|KV~aGH=26k0JNApje(KUepj z@8BS)YjS8P=$r!Ojz5EpdQcqWP!BtN=QA*gVmE(;h{2lh z06~5z)#Q7_AjnSiKnzA13x)BA1>F(9GnK=vkoO%_F{emXUhGoUrD*06m>_N(49cWU zG8&(TmQ|QIf(Zq^l-u44|-g3;kb?Q*-=1ysVH_MzY z4Z`%&tP9;{oiWucX+Fd!g0I;WI%3^2(RwHHVQv~L1dM5DE~h*un^3ZG763*EZklRCOZevm;3pMkX$kOou$uv9wt`C8iH-S{1K+|9Z$P@h2PNxuW*39;trH$$6&J*V@DX(yqQMjr)o0+_-ylTc|$x}PIqPn!CqBjUUGm()zC97RCwB2it z%P*Kek6XqU%v)rcK)+KaOqw+5zIbh0nbXot?2{&4OJfPAIs9VjkDcoAg zBbHZTqrJkl0lV|HC0uRwDp#%O;Y6vb%C#|mjVRW5W=x(ONjRyZx`H;&C#?}HE8>}a zzDQuOInBATA6~|#RhDdA<>JCS(|2tc&rHIuKQjqCc-q^fvQ?{@YAmxewXTvfMy#&p zJe4IC6%0hoxmIdB{x}R^FIs6Yw&z5!l_jNXh(n6VU72Suo@dXydx||0TVCd>5|`N> zD|4KDVc~sRD(74+aL?2qrlC1dGzq;=aTdjX&erUCNY+FsRa2` ztRbC3M}xEY#XiPsWg5L@R_qy$5qwt0j18}Rz+(IrF7n61vW#>1W;2pur#Upz(&pHd zb%Z=1gsq_g4qw(0W_7?3Bc0i)6P{yVYudXc)ylBD4ElgUubzrE4@EmZ5gZT>|170A9K4O< zPg4B6FoFSs-0h5T@EZ@}_N zOFEhvUR_wYKyH7`_y!{2;u~;+>4M z4dOQW3?|1;`Jz+465PknwK5%RQqgw`_nvwTGy*xv1#q~{eq%#1wua5W$T7i3x^u0u zc&+{ydO0Uq&auilYZ|)rRH2+S{N>|sLul4B262Wq>+w%KCm^e?cx~sBo(z;*eTQF76y$Ef*J^Oc2L!npz5u&#$L*&USZTiL zlt1OwccEr_=}oBR!%G>>cHwm`UddzbG~Nyl3}W(L1k61QZ`C->imMwSXl@;CvSH4N zcLT#WSdn&*7Yqonqz?HCyj{*2_#?wySIHAkLp0-P?%>p<*16P&%Tm*{J^m&bivcJY zeC|m>8gi47CCYIXAf({m+579OLEJ zh0^^Fy&-k02{X|I@0I0}zJ-~L3F#R%$&MQ25`8ZWLBsc$0qnU9%2cHKs1A&LgVlP6 z+^U?0(S&?rfV$H7?pSJ_4m(W^FMwH_d{)?W9t?oi&r?sS|YBIKM-doKcC2)V3_7p@qx`|211eNCe!YQe4Na)1KU_daKO* zh;cr3Ia~i{gm8`otS^@a*kn}+!cg&OMQp^h0h=PlD?2q*bLfJ-z75lL^$qOVsp1p+ z-~up;2a%x=FuF&~VN3}n_nnOiowY-@1NM>9Do%|?8z`{b?nCXp-|O~LZ|8mQGxop& z9iREZL#&q67t||v5Yex&lcvqDeslQ>=4m^E9jVBu#2}7@IUKNK5HOJE*RaDK4+F8x zCZFovp~6ITt9dhc@M0QI+KCBM=p5S^8xZ!%`?11VXYwf}zOG^G2YDaW&5#njkH$=v z`fXXPYl)p4&7AG+o+*uI8}oI6+I{j--*w7$w#3&xZ2e&GhL{u5!4Cic)g18FlWxAb-NxjZ?mg=U7GSW^omLKWBOc=oSZiCEq$m*J{u+C2j3m`&$N zrx9sOjA=d-A9yvEG&?k$=732)HCr4InD_5gb(8Nr7t&jI>6Dj8l8v~S8>rJcGcT=& zZYvn)LjRhx#Wr2l`qih14nb8f)fsbI4AUufV>VjR3B{3zqeDXT(=}1uyDu!>u%Je0 zf*P_lUyqg$jo$9bQk)Rjpi`Ej&73k;pU=$?JTzS=TyA&hz7ll7j`N7X(3Fgor0Dt{ zwj71hR&18033{Wy7$XC%kcmmYE`w-L4`K>kdW>vqM?Q=S-YsnnU=2sGl%GHau|E7& z8Wsf{Se)=@KePag5*ESV4{NG0k=P*>%IBICXrw?*fgT077|SETCP%T!Ap?1cPPqjK zu947pAT8{_(Wa+-94n%mzA~E-ffZ}BvIa(qmk+4u$>)(e-_oWzgZyqULTh0aN&6eg zqzVBTbmbs&E?ok@P4IQ8(g0-$r{(W>3CRSQibX=HaaSSUGjmL$LztP9Eao^%r~Q#o zevio^pLUi`{2fA$%uaDA?3E+)JFG7HXCg+KLTy8p^ygq>xe#^x)R}of8XCG=kk4pG z*HDDClZeQlhVH;xbfzN0ZF&&hL(PEYfBep@gs%dnW zD$b$>8k0lULaRoC?}{qw1zoc+Q(v3lP+Q^Lo1K~0q&?^sp~2Jz$VUbb*mCb^i1dnZ zFi3j${Q}=?opg8SJcC}GND>nK^RfPUe3r&Mhkh|sX{4H^R7mZZ;0#b-lRvZJ7y`lu zD9;yYd$mbCnZKJ{rL|bEbEb}CEBh5sLkExStO(SIV zi9%B)1u1G>5RGxKnfBmtz(nvDWY7tdP^n!$Z<9Z5oP=^Q;_<#SS8Z~)^6z9bDGSC2 zzwxIaaIaj%m?2hd2WTUSxE|>!=d^H6-y1rmoK(l3REXqGh|?rIey?ncNfy?fggG6V z1$3+4fC_~yaJ!Hf3;Hz5PN8h!G|9wjX*-o8!-S~&MRThjk^*n*46e1%OU$sg5%m`q zSQ)u%j2A&@quga5c5+M;0=)0@*lc8a90aQp~1n^ec{{l)xh8uqM1Q*Nt9yr&jNmUL2V|sAKn> z8BweEzQy5Xqz5l1yxie2k~uNes~fc!GjIdLI7-(){OTo>5F-z#O&`5Y9tIyz)Z1k< zoUdF$@iI16+tIu58|m0ezuRG#(i+LnqGLMqFr<(X8|!Y3V9sl`QpNtwioth5=R2hf z#VPGD_HtV2Aa&L7iKwe^L>5XsZQ082C_54F%g{@fTx?0j!j?(q&~Q3R^B9~dcq_wT z%MjWS4)s^ONZ(5GgYt)+D{MaFP*S}r^;6QpVt5pk-w8W5aFYwS#_pxgk~LX0WG$DL zV*d_nuFBp)ni=WO`@YbNck${WF-r(|;wb8=a8y5Mp!7$f9|1devlR}BW-a%L~ zj5Q$bG;AKGW-S*@S(DlNr@$eC>bnH{o5!*s#~_39KHj3oJAUlJ6SZby*dM#`PZ(%;DqMGjS>6v zuNk1b)D{Nd zj+}R>hm`+DJuSx^nXj)Ord&m53e~!O4=rTiYPwr(3#k(7e(cOj9diPPyLIiMLL#NF zkzS#aQD1gPbJlWp$9~gM$$)14y=H-^Z+ECkA7*G>lA|mJMoi}A7Sz;eIr-y&;hx;_ z-wC?cJF1}o(GD9#U+sv8Ex;%)Um;4^-;2_a4`6UGDrHy{!hlbMWl_E=rY7?X95URi zD||jLx62p9xh_uEO19F>n??g=BC$n&evygQ95MzJI7 zSXs;$E>hc9@XPYy-+ZE+L&VbJaHrLV_5yGA$}%9usmeTzIpJL(`E?9U7uXlqO+edb zzAh$jJ|-n%Hpq7#h}D+YPAPwYp$NUN=A=iC>jiPl!>7iJCb+dT=W?|Zz;v=^X0?+5 z)-4aEvNWu+%Fj?5nV1xImkl@=?Vxv7jCcey?c_j>PJY0Q;}kJgc2{c<$1yQU`w(Ml zn@aP}u8V(h;t1a5qe5T_Mu=}T(%98f?hezG;&>W5;Ag_tHQ~g;vY>xnx_Z!?{a`6l z$n(<0{`F0d#-e*Zgq|Js z+b1Xm;7urHgqcPLtg@S*M|vf@2RYMD$bSPe^eA~*evG7nsjM=+DMkul=9)5PQmv5Wq~D*p*J@iW-mhhl|dTXW3diU#U5bjNVVM^Ao9hem*@ zFC@RoxT4o@LjSscpQe<)L)bnmjxNmljhBHI{8=ZEP3*69fvDPq-MBBXUK{!V>%54j zCzS4cG(p8h+!_SHf=WkG4)B*h3=Q?q!?<`FdN^R_JPv0w{EL%Y3^8O~h_B#FF!7HW zH-JubMS0Ju-&j%R92`qTeHvx`(vj4#ZG`67oTB*lSs7=yX#RaXbHF6RHH|TSU+-2C_FJ6ZQe} z8Jzm!{{Xb38^9uJAW-4{Tek6 z4YeF=!2FJBgz7NDLW(`heu(?wN7T!>~OH zUbEo)@{|g**+i9^k_(Nx4cp$RVyB2H093o<($m8DkW=?MO(?K|@&2V}bLFY2J9W7j zt3v~o+ac?aQMm^|6K>Br{L+brkQ zcA8dv7daip=PUQ2>uPgif3i}DLbQp$#4C2-!^Xff2iqO#%2Xiy(>~4tntc1SaxygU zuuhSoQ*tXQZo-mq*X#H-7+%^lfev;W|I>;(Nyd&QGK&UoWnFWqR+1^jFR2n(cEe;pfb){*!iJU%LC7VoRW+*9&RSLew;LuM=L2j(P zU>NbuHiGwlNTq~8K8TqjRv4Y7$8s~@sGDJFyEkCa37L7Ox)k(w9F#l`7yYYeIEgll z&S9a(CV#NiVEUSl#J%mHBl0a~C{w6en{Lu1FT8;!$d?w-y0QE*y246Gl}shc6$h>g zO|Yly2ht21&L^y=6bg-m#%xFxZ+mwI7N_Dusav%)JWQTM7A}rC;UWW?Zx>lLPtJ zupC=QYhtm=2`CD>&3e?#mu*-Mek3+xcK!=HQ)G5pjd7q#PuKuKpNg@_oq9j1NxFOy zQ}FcgYM6YT@SHz83%n8v>WjS#6ROHY7sZOBBe(CX4MJdZ>Qh7)t02W7Q%rf%iH4Q7 zJd(92TWW@OSVxmp>3P^Dv}XOe4I_lo*D;L##)d6Cb7a|X2Lh0qIG1b|jK@ha2c&WA z1nws01`!dUqy!9c;y7RSpk+u0&wu7bIU6qn7bwqZT`-v#Q`;r^Er>3?4U zMg0%w2GRGz%adUqPx%L&$O^aj5HyyQpzUS&@9DUh0JXV>AL@Sn6k4eYLJKfhpwmGC zeg%>pFJQpvIDZza;ms6J*65lbUxta&Y|qZD6+#~xmUd^L;rW)IvCJUla85|Yn@I0gk=-9TMn3AKg&uy$enP=0eUs@(YH0xP4GfD;TWvC0<^ z9F9)EhFV}3C6kwf4Y1V52vIlb-N&B?zY+l8wMNI#NO=q!UfXyyM0Lb|! z?6BajEX1C4`y5RFVxS3(yw)Hu#gH|}+{Zf8(Nl+vOEA5L2u9gI^4l4Z+UGjxX`(;r z6DS#9|Be{v&E8rJO5*Kg88x-*l+TA1N?Yb5uhX0D($d^}87t0+wv9fk{6D~>E?}SC zOkx~6uCB+djF_{BfTd~oQ%H?feQg1LO-;f;8x?|*~oxr8CF5C(;PyOU+647&lly& zUo9-m&HRr2>9dH}2!b4z_V0jDAZM*jFr{fi7GcvhqAcCD&x@8sY1juq+I}RXP*o$v zX>3y8_!BmSe~vsjj*C?xL3h%T`H|;4j1T!?*}=)8%pzQuulLQEiz*&dSOdEGy2J47 z;aO3qlHbQl?f7qmb!};vp2dlXnZ$%Vasoh~J+$D0u=viw+(*YbB@uItR^z)s(X%wT zATyid+1%_e0Wd8%qk$xaKq)k;OUUf18;CV1Vcn55-$U^y7zwKot;(C^2m7ZrA>D3> z1c}>eB1zZN)99qTd>PBie&bV!Z#B?%0yMk{<%M}#0(1{gv{=<@oC6d+OM`BRLT z-{fXX@K05F8nh+u=W83;3d7>GpjVD^0~xW2`hXl#ktT z4rjS>I@&Ol_nnFrXR*5yhT3p~X^1VOSv>KRIv?yUHjuPhu47Uk)Ck(;nnR9fLbM!$Rz7;>YL{)26 zu(hxsMGs|nB@P+i9t1&5i-WWomW-3_2{c_)M3^MGX(vF@fYc)Lj&WT_0GpH{!Ry98r{GlABA_v z?;&Ro7I80GO^i#HbA>`+q0Xs0#O|^)+53QHx7FiU+vLsu9&e9AvNrkT<)iGbHukl) z{_RXE*G=9^U zO~}A+y6y###_+z=vztHdXN&ukmp_E1umwD-J@Y%M4g2e;f_xAiX0hCyO&M(cqpa=w^3rBJ=vuQ_x9t1Sb`ctDG?IMlENr-k~e3x*vD=1_A#iu@6$FZ*+@h9X?DX=nf zck}sv-p^(ycunn?5t**HNne>1A+8H5XX%U_&~k)#M(B(dE!t|wv`#MQ?Nzv{v?C8{ zfF&C$rU|<6`6BsEslNM zZ*z%1Ra{Ny3dKzM1G$-fwe9$4a@*x&qru5JOk>x5lPqLbOy{JJVC|Ae4;6G+9$J7o z`7!ZLS|?JEg9c8lGE!T1@Dv7tSZ)^z$!JNa$B^FGD-D%AarleOSY4V9x0xk_(ygx>=)Z>t_NqJ`xT}QI)eUm{?{O3Mx?7&^2L5`G zcp15M$d;jGWY+61JslJR1z1$c!eRWWL#|zV`f3o4W;$(u&opkygj<#xam#!NWM)_5 zm(9y|1CaBA!7qC$-Z5#cokA1g`}iW-`%1&Th!hA6TT?TQ-(g`?nr%60^QZofj!_|= zL&uujiw2sLsvV{njsFw^zseq zf)FsUePi62;^OPknEAo&+dGZ#Sctjdv^q;W>%@VY^gyI294id^jlZ3$U7}-ux;h5j zWb1G*oAoWhdobT`{8!|Mx85IvL@Cm-#?TNPf*VA1uta%8O$v+-*Orc{;(IV7QrZ5Y ztKRH5;@+RM7}>=s4e1DU?teh+S0?zG&>dJaV7#9h#3cWKkD)v zX;K+SZ8JC zFb3$|n1fn4aeM5qVmEG&RmU_S?9E`hy3S{hOY62C+8o>s$2 z%&es=f(=hY&x2#=MoPAJEd`UAH%R+f<9%V>^5}w>PyA8|z-r5;wQ4am@CvFE`U=Ni zriKRKH!T#4-x2Izn>0FZ5E4HC8C>*i4f1l3-}E^OW?^%il|=nY}az z{FxAGeGnfKVrU=4B|`M;gXkv2ckq}Zd<_^4zLe8_5NU)s-UsmuLhSE@c#IG)_Cfp$ zA)e|%Y~P-1FXElq797!+O4n$I_-AmXB~^3`T|;r>m~k^WX8UD*EWSZP_W|gjzk9pP z4C039266w6Khg*7Yj~o#G5kW`@C(0r#4=ol&|>jgEs0FtLMM|mKhKeuN?`uNA{>Dl z#ps&C|6)!>|LHG8{~5ZX|BP3A|MmR}g?oE4wSJ!m{YrZ}zx#H(IEi7NhXt7ciM|H7 z1MpqIuYms=@G8P43}+PxCj&kNm<}icKLc>bM20B>{1$jO;9Cf90Q?GY3*f7OzXdcP z{ht7zMEJh}ry~991c5&a_#yCT04D%X&c#$TMt`_nrx6|rXhXUQfImk#3-E2g9|JxK zNSBo=5bps@MZV2|bSL%K6bAke1QDi9@NW@r10)@L7w~D|zaoh6_kbf1j>VXK0O7j; z6A*R)z6`h;@G9U>0G){60k{LofN&n*hk&aAHv?`0%mUs20z3_P2yg`W z{vI$9@D9v$uOk25fHtI`3+Mza1UvwI8DJU0KLs=({gZ%ifzC^SZzDbkcpC6sz@G!( z0ayh1Enp&GJmx`f1EvAiqxUTs_Fmr80^OO?3s!pz0jAnBWu+Rc#i%9;unp>fRw zU$LsyzBZ}FAW&H@5s!To*RN1E$vniS6%0-byrlcXNn*JeDHK5M;D&73VR}C z=$_O1+Hy#lDZ*#8!ouj~NfR9!+NCj3Qv!x7TyBVgDou4`7D&}%33xBXwIS5dQBmPq zjk`%6munqUfSXkltE+G)Ol#?I1NJgQmGdxjYOB|QshaW{7n55euIbS$4yKsoT~=KQ z>C>AViWW`-@kte;rdGAC8ib&5+C`{>s9eiXw$=vgwM$FYrKM7BSkBr9AbWHIEe_?Y zt}TO#l&*s)>u`ZiBV~(frB;lqvIav(k03o8D_2$HW2(?FRDyF=X&+&AQ6qz5CG?N3 zRT1|t4>i5^30cNLH^j=_<+uP>3oWWc>Z0;Wgj^eHGzH18trn|Gt8urBz9h*kDyfiM zVg02>Al(H)H?E+SarAMm2-S$pS8c@iYCX_3aO3J_Rr0uMJxn2L=Yl+Yx}?@$t0sCh zJT#N;-i14BM2Ep*L~U~;)#(vh51Z?2(XYav$0gn(#CoD>jP4ca4QP*4Q&U|_)*!-} zcH@pR^v2w1KmH+}v{I}ssqzqEG)Y-aHNL#c^c3qZ!8d(3YOUEziw|21+?)K{NNyEw zGe%m3i6$(mpbLW=>B<`<%jAST5&B~A>MBiSkR+NXY6$SfzV+3$>$q|c6nT9W(q}~Jw~_ESq%B#6sxvvg znYA2RJzQb)wn}olp>Q6pFZZ@h(cj38IF8me!VRhGP18}c^`?o41bq@>aKp6Rq*XZ2 z8VSx81k%%`YF%KARM*qv-+AbJ#gK7teMEGZGO`SPk;qAcV1#N|`yN4~Vh;vPJu!tME~IjKa|^>(Yz;0`;>P9tRcp~cR2ubUkqo3} zmZ?`px_=*<5J{;E_i~x}>#HD-G8!&G7u=EP$N<4qYl@H?xnc^!6h$h%2C2eY^goS< zkfvtkR=G$;#1gGvJ;Izwg?GXTchYz6=yw|PI50Wd?|*2&GZ?z(mCu|&Y=M^M1>;U& z-Znf1%wa=1-rEe%;C-kT`%9(^n5j%Y#WMHfeUyfO!;n7)Zo8oi_;&60C(J3lf2pV6 zGrD5@j=haTdL2W*jRwMW^()5v7kV>(M{9J>GK8@*R5~_4@OzKO;Ss|I;GffYGcm+r zaxDFBH$0Bt-)kw8wBIR=8JrB#YWJ*`{&RgY(u~kzpVu&d(SAD^;`x|iCrUZ0(Z8Fa znjSY$`gu$S-f!1(ZDi=+C8xdHw9;p49R69OGg!l?Xul)J5zo>%qI}0Vs{7b+RNsZ; z+VNgEj(DydM>1ch{Z@=Sfp>&lwNm9 zG~aVcx*&2%x+r`}I=+u%0@X*yn1H@nvvC54Pjr{N0inQ?HLuGf)|Ri*y2u)+ncMh1d3Cc9m@l zMGaD+Lyq zDsw)ijxU;DfP@PnRV~8=jxTT&Qi+%%Q*I;#LD0xUb%>%SZfX~Bp_OkhK;?6Jjq2PR z#g7?^Tl-EOEK~*5P6*psgfL2qE^=#gOKNJ$t5##jt}m8?c6@+4KQd>)45&i#tf35Z zw873p>F3tET=uHEayTqBJ1KMVIBlU= z)*}|8RH#jf$c5xRR2IF3Qx?HrC8CCBoGsfigTD^wHyoTN+SW^o>x?WED#|~IkTGGEZ?q2i5 z@vE!X+__}*Prymd19ug9z{%RAwB%7sQ_I}0q2=IYOu~<>%SVi5*Cmeg>55kl>$md9 zKh^2(=jPqFEM?BZJ1SO;)QhvKDu*u~bVfHW*$W;xt5-*b>Zx#jZ@E1ZC^j#?Q34D9 zz64f{T|^QnfduX+3E-Ax_=!khc|-yqlLWjmUITu;+F8{9k%i^v;u~04Qgl-mmPQ-l zzOl=|!r~QRVddXzglCBbLxh7~;YS%(`!vIU=bcF~Hsv+v?aTY`yjSx2G0*B4ukQ7{ zLwPNEA=6*;Mn69Auevk(Pju~hNAs|}l(!>q@kgeEdH$G9tfAA?kar|+f8K$-VBVWC zt$91+Vw!c#K&&|BjnfTp%hUf-$Lz{`E$?H~3DaxHozR$9$?VPBotN@n9&=^V8+oth z4cldSH;;KM@6Eg&y0`OgQ%BLiX#3ra^jNUh@w8z3R16gPQ)PL2vr8k)DtL-ip0p5&D)~`g|DrmTuhA4F=CGJ-8)< zTY9jWMDTxOUxf1 z>t=?oI{R~;^PFcp&nfd3-8X%MC;O&EL9h!Qg7Ie(Z0_hQJ!a1BlIZf$ zgxJ!SjFztGEgyk!LNd@2wGj#>hotm_-4z&mV>r>*U??37vtXSvv5J_z=xVbO^%&S z*CLa;4M-{PvCDL=Y0_IKYP_?qXoRZj zOVty4-RcqQ$NGgwv~PD?gj&J!Y*obHefIoc)c$CMY8CEUc>HXme7UOCpFC&2>)`Wd z)9cUNl@vU?=$W(AyqhmKc0up|!t1=JZb}?>)6m5A*+ZRaMeEnDUAAtyV|2M=s55=& z$f1dg9h-(aT|*s1vK^a7k6u<*wsP&tHOu_PjwOx}BOLzC8;Tvv@Ndf4<;CS=*9F$B zag3WX!bN4pn~IBg*-+>Dq3F*2pE}!;=m!j2=H}&lymKav9_wGb;r_AcoZ5}vgdY4u zG3wT>jxCAHmpfLLpfWmMPf!0Cr*o*IZ1u_w*+U)tZ{lr39YyN{>(C`nyT)Y=@!0 zT^z#})?v$k*MOI=UA=r|sblm8G}_`HKKADOhkkbs8w`l~DjEnGG2#d2K}%D-X{F!c z8kf$=$v-qUwTW3%&^bEjtl~=CP`YxRzr=A98E zzp=7$>Bt*@YoRM0B|q@*R?4HEexU4sloy47_m8nTMCs#={;2G`{odr5GGd~!iW9R& zjQ)>g6{A}`L6aF>DfiZl%0gTifBrQ3{PkPWboBX~=Zy5vo)hABJ$s`dn1pMyW}n#Cjry_+f}2=)TM&j$E0pB{dPh7zF@#t^4pRH zC31bqUi2lDxQ>+Mm;8?BW!C%0mLwSIIt&slkvmFeh<_@{?AY2D?|7qZN6C=OC7G84 zW>=)Ku55&Q^ithyVP|uROD}O9Eva!};F8^`s93TmU&GB{~WM_o!JW?lEYwgw%JFNLTEcqjv ztU{AT9}&#f7gSpojHtE_4!CPff;vjr_3RhR)#1Xf7XCPKcfOh`?0OrCUc!Q;xG(Nj z6NFv=Kr&9y{(NHjlW~E|jd+;+mm9A%4yqA0?>th{+)c+dG&DtY94qPZ zc$|Q|VqM^WOs=av*QL9Ty3WjF&CP-py*NJJgM}2-vo=9ku?-!4Fv`hl5)<5j zP7w5C49)SkK@eEQK0$~Rnk_(BJkNxNa(*xfDQ@8^fytjU#k%z~)RS{TTlBwqs|K$U zR@9t!^LLZ$4Ahraj1M3pxvqn`uGU=F(YD?<2!gUPDRZMOFaQPG6g?u(uw^#+zhb{+ zt0SpQ@xhQ)P{>nX<4^!(F=Sjug@5TN+S=Z4qVlg}eM39~?x zjwT}C>CWgQ`WvmmQwPi@A^$t2|L-KIUx$I>AM9|9zj%|Mm&8DhJv*!4jg=d-e3^e4 zWu3=Aq7ecD z{B?A`{x@&b&oA+B>C(Bm3%+A_xQ$ldgDnOt(F4I0gJj>cO{!4#$H_yM`0`rDM z>ii?(>hec$sra{asYjYCbt$k%6NEqdi@`^D#2>*G!x^QQE{!3aQvXeV(e~8eci=Lb zUH@IW^q#V^>!}~;&k=?1SZAfK{l)MVD}^8xNGPQ@-}Ktmg!%Wyo`l;P{3EnPw=CZ1 z&&_{h`;{mD7k+*`%oz7S4OKEm99+pO{r_)JHLhbLhLuO=dF6NHH>Eu{0Z9dHuW(-1 z^=*RSlaF}hx7j-#!*!5|FPzjQg)S%gGo^NuDR85^@=U-A!KRH=Z@HxF3?}ZEEOfx^ zl}%p7RimSg6gri_>W__OLq2AvCha+&d|>gCM(IunK54!*PnzqMzn;g!>+JWH+&2pL~c*Mbi-$&WBul|H5CGf|u|P9lV#B^HW*HrvxvnIL;p< zU-$QxLYwWTKzAlHyQD{$oS*6q)u(EI+CxMw?Iv>1%{)*(-z&Fz=lxH*_kMg&(l9&{^Aszf?s6hqXBQ00$@R?_=k6B)y)68c@w+26W9A z{-XVbRcyh&wb4$o2~5n%v3um4q~;vHJG`V73PKP7%Vt)TljO~8D!bFCtQDA&Yx9{@ zCR?1|aFrl91eR%WmXEtVJU3h5F#F^$nEZ)4>zaXVv0X3)5`FS17JA*xWV^RwKQV5t z-m5pU(Ee2I@db;QNQKf8=`Lw0knU}6j*P_o=R+@&u;N#xx$_F(umRlTolKtVw5j)? zYj1`AI5xK!dK_>{N_?opnKlQN}=UP5(E9o0b86g09qt}L30yI`(G|Jylvlaa&Xn=a=oHWS zOvk;@y--IjzKWUdj!`2U@-LR4F*#Lx)EjEfXe$7b=4A$iJo(E!F((htJb{Jw*}F^{ zT6be7x&P3cIM^cy+hBagnM?_o9si3HrWi8B;^1jF|NZWdTgc#ikaCHjh&OirIijz| zGlv!zhV8qxC=w{LgJXI`yw;`aeCQ!*h6vcRL7R{&aljna)$- z`_X5NpVO1tQM3P`e?3C_SG4*6`0+z}1^ZvsD;x%_F+@cnkFD4LZM`B&H^G>Y1@gYL z&Vx{dWi@AMbb1D@Kx=9v?} ze_?9J*$+x;elzMLqV*rv19-FRKQa2bp7HSJN8f(H%^bvXQvP={?dhKZx;3)0^G_!T|%nqAsU+(YXo?fnQ?bVWP~ zN2J-(Z2cW+p|o&uqpLNT)mQLanckp%OWj2+LXY!Y9WIY>so|#%$=9gs-u{K%sglE=Cp-cTYnnMi^k64>z zlQQ29*qC0g9hG*OAV?dD+keBt6V6~kNansVPrh2fiarGMcc{z2Mt$;GFy}k*hFUGC zg_M`)e}T8Dc!T$08_HfW%0}~?ZAll8varwDofRF`5}Q2E?xQK0N`84K6@LPlFb}I}4evNWy_SC^5ij z))>AdJ1p^?2=ysFdO*y1!*gA6%+$atE)$Dm;goZbk>d7W68um^#NgI(gU9-9O`dU% zu}p9BDb|ls!FA9VP9EpYgqtRJ#{gSybz!1|Fn)%aC%lYuOu==KncibiGm>^Bi`8>` z^73P!7{KJvi3ltc-Rv0+9 zn91i{?OZ=-vfhB!f^=8g`#`L#9VP0Uc&m&AX8Dxi?P;X@jlPJ7>!`HDG9WKJegFuw zU)CglA69Wi$IjVkm8v}hG3`d}BPM@LJt#&GJ*422Nr8mC@bUpBt!|eoCY_;>7%Cui z5cvX8B7@V*s;snVH?k_-p9ppEH3z`bl3Ztkmu+tG(f#$Go0($vh0AQL2-xwz1J%Xv z#Lrhu83#4YMoAl%EpU!H>&=w=QfUzrm^>k46uRo;3upKHmgqe@4QZ;>S26jNm3E)9 z%qDxBmOS}fTQU${btW)|g(eBFkx1|lV@e`b7FormkHK~QJusSGYkQ5BMzg-=#v{}b z_rO%9Y_YpqePQb!@W>rW{iGe^?e2^(0|}J}F(Yh#&7E;5Fpq^)vrj2X#Z(+D)R+og zuzABge+VId-3X!7Del{TAK2TCrO&_2ho& zxmkRy!6#n}C(n@*4(}m7%Ok6LO6n~{{XG&Or{spJh*-TC+$fuh#j0OnXXMFOvD*SE zQpHpf*Ma;Rl4zWCwSLGX9V##xtnI2vd<>4Su*YpqXi>I0EqcnU*l2quW1pgj8a$!Z zpTtJ>$?Z&;>$K}Bzd;3`+>saFP6K*(#|{R(nGx~9Hvot)(2ixj*Pzj2~i%i`Q(?faoq>EX>S?w}rD z-VOERw1HynCUbc1+b!W)Ex}9iBrHW}w-XPahtsnY1hMwD= zMK}5Z|8TE<$QwGB0V7JR{0P;dt{(tt20lZ@52<1$DteV|N4&}`)vIhg&*To4aDXM8 z_jaGr@^`YT{7I<0kLoT!U3Bn<+)#fiE_B@VWSrj|dOZ$4;T}xh>a>Mkw=mg<9SC8R zF^`Qr#7u7~KBog#p%glx!=`Ym&v)qhA}sR&7>Jwupl6~c8H-P%13`d5Zat&6o9tnIH zy6WJ@n^y^7!+r=!Z~`Sv^jwkKx_Ee9N4t8Tnf$J+b-t_Jb*2&Qxa}6QXu>_-z;%dESg78n zz2&YR?H-19uubDx*qVmr^2x)qDND9a!2{AEm}LQm@=Z`AtPU&^) zvxTwutXwx~Y@n=k?3(pO%hq%zkuQuE0%gnYEuO?ruN{v3IkRRs7E5zx%y*5V-*KbH zj2W{e(P8}2Afvm~N#}K3Mx8^?7rj;4@Y12@*R0v>*l_5@(zhxPJ%7)dLoYcFy|n4j zOGOPYUC&sve%bQkQb)OK%=*Nl^&2*quDo}J-+^<^agJ4iGXKhTIA~u}ya}iCrOO

+dNp^_MyPfpzPOHz%&}`!|$L8ap;xams^}TlL`u! z@LImv0>Tr<;BYd7*GyG zKx{}GDXd(JUSkQGJB?iC$e+8^H%s#HP8}P^I`XG4T_7OPV9jy|NAXJZ7?oeeYf8Qc z;}5wYN*;OS5okP2%suIkd)`sqW5QJsv1{+9*^(fe+jI5rP=x$JZdm>lV$h;rzF^M0@i}(5&T=FFJMpC zBtPyJ@Hq5q{#aPeA1nXJADelZPd>(;_Q@*!bkNU)ap-iFdlAmej+_Q}*ofhcpaB_3?-CU0n}5U}jBYXe;ey%kw+k-CMnTkGKsWxXZb8}`NpPVO?Xr^VMIdz+kY zL44U_4qMFnv3Ps`p|_0BUM19?!+f;yt413yx8P-u4LL(sEw24l0$%#Bnp``aa_p<5 z+J#g(e$|B9aJ(ceur{BfiQHMI0x=c(FEQ&f2aXUAx>}zRUv3E=-^SiK&Jx}U*2g~? z44h^^V^3>G9$9+V;@t<(V24SGZ#Q)d*c9uN*RY6I_Vgon&R72pwszOjn!6cLXnGhY zh!4Ay_JC6fa=vBv-5FRonWZ+n9VGU_pL+~52?rb7t)s5MBo`~YqvpJtUS|MT=yZCO z{s5HVZgqnwEn6#k<7oFU(*&V1B36BXE^E)AdR;n7)i)V%yL~V6rXaby4@xCDQsW*^ z12$iH!Xr1;%|Sw)3pI{~A@*v6R51}1;bx@__0QcpS^Jj38xCFA{ z?x7}<4VQWWj{RQrqfE#{R^4oZ;6Itv+U?|nG<%uv_YqmFzQ0H{AVst=;-G^V-< z8Fc_(P;cFW@o0+1c(?r=jY9j<%P_zz`h7w%2Xk9$kxf5NIHmVFal|GBG@m@w6=}9^M;-dp@_ga7Cg0@IJCIS<53OZtEu)?jf@}0@ zq_lzDFx_KA{n{3cTXzIMsvR?g0DYs`Iw{(jd}NVf95vF;l;m$=w0q_Eyz*P>Y}hO{ z86ec%v{e~`4|?U#R&PL$x_#jS(uuWGYIh{nCP>6zs9$d;t^Op;dY}B2PrlSxw+S{% z+oqR|T|ei}g&?Igy`|lHpaZTwf6@-IDZa3JmZ@n*T%oH~$?M3h7pFJC?NJT`I9@OG z+e(@(@rBTXnBE-RrWXb*O*8bdp*B|IL8K! zao8&Bf>q{+&2mnvoa2^rR@8K0zoT9n{^jG}CT+@-7XKt}11h7RQ_kX*2 zewbRU`eq@2$zSy~y@d8TsJ&ucgssw?GXNM6LI+H84m|E_;QhuXI(d;Mn98?XF;gtV z??#;+!Bi|@RzkTnr*6SH5NZS@03#X7mm`?5JaA)hlPM5L32us88xVt=%=ZKm;KA8s z5yVFus#6AyGzr1s{qSpA&{&-!@}!NDjrlbM0`+56|N35?u1?4HdxM3in_-;KX4&Lp z5Vu!S9r-E?T{8uGl!T^^2@EP}j=x#ZhLoq2gtl(HR>;DZ+x!Uz&S8X-zQVH2T}vN>mU|-3-aKsYJqP73YB_g7TK-p zJN2Y)odCaH1W!OC!5Ew>_-_YrbvV2b)DKWE+W8bFH&Ms`>`S(;?jOL4&Qn)|NqNNJ zNj~{;vKLTY>rDl}FvbXeZw#FJ%&XEjP4E?+^i3W<5=YpQe6?&u-s%%+KJ)M)ln!M% zO@U+sf;-U*llNnUKoTH+D#9Vwrh1{K+rgQ**G6tq&%@^f@bI=1ieD7wUQB+HVDbR& zYt@)CJOp^VgOG5H`#vydAfRA&ur43Lb2~gp z4caLsw2L-C_@A^^I$38MZ99Tbo^cD>AmwM2xy#btU=Q@dkrwC$zM}pVwiJpaR_Dxw zhryXQyO=ttpI$#$*+4J2vDbjx@N;2DPQ)= z=UIIlS$uFDfy&gO8JHVZ%c{13G8Q$$z0=}@!>|67BTugP(s`Sz1Ad|zI8~0~C$kaT zt!jC!+ zppKq0=mb*W420R6#qF7dYq*zOt-QIOQX4I2;Y#O|Xi*Z%a0C#!9EsDu1}%uM*uyhh zN5;BXhgC9g`o{XK> zM!wbNTBsI+`b%W8)pMQc>F}vgA1@)pBVS>Y3!Uj=_;&=w>Z9pC(*-4GfUgL?r`zqJ zW1_eIm>oWYCEl=1L(~zpKG^fN68JaCy})q#O7^{KCzwDY6o`avhmqlA!stV3^c#2~ zn(-PK-Y38A!$wR;zBVAZWu%EI1rDElus{4gTWp~(u7#SM>Iq0HNpEhO2LveA*K^KS0AhbLXQfDX&IVdQ&4PAQ*S211fmo;IrycN{e(|Xw7;>?>z8#a4^ml;= zJ8Q201}TzeOrC}V>R9gAMlK}cv<#kmJI+Pn>Wm6GLb`M}!olDzAB;ojG-f&w$}k7S zX3SO)GW*Ca;&iiY28a2O+;Z4pt?KLM~*$eqok z`nlEnUPV^8_y~B5afZC4(HF)kF>N|YFYVx33U(q3zxD`LnvV0OIR)5`5p2iistn}O zpZeE-a77gC^IH(r9LX!8ZeIBM!Udu;Kxhe~7!;=HN<1WuiXVu)ThB8$V&-jb;@(pQ&>fI?F#A@YMJXJ%{&RAfCOR^Gx#eT`9X;y7$9H%G%uv`j>( z>6Z=e3dgTD-B3&;Fm$LWg%|lhH*(1Uq+H$b-n_+Dr#y^}nhquZmv~aq2^5JhLq3_j zMTgnslnux)8xIZq0mLn)Gjqy%WGFKSggzB@c-vXgIG-}jj+U(c3p^UsKcfTMLJ_o? z`9mx~tuRI({ezo+xlu?xs>57-TQ2s7-TBwQvDFA&}&pf_s>%Ji+Tl`fi?}ur*2tMqx?Ifa2l$a(=FCv}LEAmyeBE0FW^~MmaDkrZPxKLQ_L}N7Gsp*Vq)r<$Hh5)Cl8^Xq zDRkOA@+l8|vZK7=EQ?S6n0j*feJyta#ZNJ zNE_#|2%TN^l(?M+5j|3WJunnYHwY*}U`Z4Mi!MPvx-DiLS2aP@Y2EOHQg~H2Nd>vG zT1WJT1JXM^ruU@z9t?s*9ChkePQy?pT0zED)tIr`Z#~})WQ--yoa}dmlk7CF+rwGO zm>A}S4NnYxE}*3usTZ;4#$@~vA3#ry4d|2KWA$h4WcUUf1!pZMcaEHbze>VVP1A$`X0_J%9LW=ui0hh!)>;i2_q%?x{ngJD!7 zGsrUEF24oGGmQGuw?Qj>Ai-M!(Q$3t1AU;DgLZkRdW&Y^v>Vz7P2gDUs0`6NF2?$}m-{M3H_O&brv#zcr;OrePoA>Y zfq$DmSo`YrNyOO_Vy#akH++4zutvPUCs3YNlHH!6*=y!J; z994Be_=;RdW$Oh#xP-xZzMgUiUcnZZen^iA8Avg)R#^hJuBW_?OeSAup=JxI>T*3C zykgy>H%5s0>Wx16Q>dk|z|Dbw!D3GXXC?m=`RVa$2Z-&~o`3*`-Cn}^b%3uMc0E`f z3XDi@`3l}G5F>76>>;_r;2!hJmvDsP@P+>@2-kEvUP+?{UMRDA%CrxNyDfL78H^Ze zLd_Y1xPA|5JaqPfO2aV;E^$2J^d~KWzP@nYt5S`HSGbHR=qU=#T|Un0&)Ffw05@_W zFbb)!`pb52_(dWeLAHnI_5)`=2PUzH0`ENX2Ka*pjG(TX-Dn3FezUa~nqhAW>Sy?Y z%!5@_)ZjH+oI7kdUQ7yJDh!yLgt1TnPUwDK(!62DM}*x)ZYqQxNEQ4;l*A9;}bP zOqp);!oxFC^<^IMiZdFUJXTB>=2fK&*!JoNyhZzP5a|<2EO!SqFur0??< zz3aJwdE|lfMl0CxrAu zvFcf*!0`ZjVW5Xc*40MH6$z_|=(>Z*9U4efck!%x?R;+4L7c(ruOkps_y4-C5N*{m z6lxlBOb^(NoMlu!bSh3el?&YVs3W3PeY}r@=t11M1hWrbG3}+@E`>f2a>KJtQs#$S zMP4=3XjTVNr>7Af$q&tbb@37o-yUsjBc(=-Lm45H6#ur~$N@gjLiujH9~p~1QPY}- z9pZFxhrJRo1aUS?)CtC{oe^u}!>mqjK-fyTEu=0Bb@boZJGjj>DUcN07B@D~t@=(= zQ~cOilw#hs;5MRyWn%pKnM03_; z3>QBM?y4Q}5L4$lHC-fXf*lZb7_#spxcN@Q%t>;Ry<@|PZk7`VNLE_iaIT5M42bcU z^`r>JyrTWsb9cyDO?2GX4q+Eg1}UyEN#ZM6$%t84DZ~HHse1sj<0{S(O{37099Mrb zG5l&gQMPsi9qgrq^lX2R@Qgpu)NP@24$bU-*`)scPsAg>$_tm9e3=(Eg0@w-0JWdz zRC^}sd|!QvVhyBvq1>9&WSK~{YqPP6J`m26F!V?;rE^rxtTWJSAZh?dRNo!>W~_wY z8oWFCCL~)`=!|vBhgT4NpMZITS#nOof)%MG|q_TYe8tS8U5~ zVj(9$A~|3G35gVQzT$jzxe9m?*sEl(!W%X)tG|S;+Th7NUOo_HVrqH&2=w&T?dg+! z6D<#P^MvmJLm|C*qz$+fyrwfAFyP54M^3`K6RJj5R4;uW8PG-t;wY`Xgj_!8Fd$ zxhtN@=UuJvn;^c(d+|#|QI*JZXTdP|iX3u0j0%=QW~6LTiIO~{WSTO5Mu}Jp57hV> z*lQ)z!?rOy$(F6F#^6IddgiQVf}OX$1_R)m(Is!xOer)dY^4oB!3hZoy;UO=KKHuHM(!ZWz0t_EpzZ|3Q#rHXUe6jURy_^~r5$sfCb=ba?30ix zW%b`!DDCh#<50trH8#+X)f5Va!c_cYxOP?|iM1Z5NlOKYr&m?0K{l-0F;*YGeck)5zv1272SX-hw8HhydgD zwcb?O$3{saksr4uwYq#PgDsnrblkmQ-0OL_ltv? zSOkBnBsXEER@c$n;W^J6`3*DeZ(9!ni0!Aw(Cd0zA~+f;6%Yg5RT>im6{pi|=nf7z z^bdAVvH%994~7wZZ#wr4z)J)Uc947#W^y@jXD3{xt1OGuvBQYI$ww_#M!r|CwU)dU zqWU6R!~AhvuW3m@b_hMuz57(N|ibR+v$OIKA=CL3@detCp{ z=*FbbWow|Xx}5qxrr;utbqT6!Nf0jncw~6NX<0KV$~Zpq${#W1M(x#R zt0xLD^>!oNy6Dh#5bOEn9|WNZcYUOUeQm|WfI8LTMpo1u^^Z&fA`}W4^{0e7EPjiA z05N4ntz?(BJ%K1qyt@v9)nL~jQ(yiIP-#fi&{R|4*7g||vwt+_hRQ5NPtN-nr&}C& z1p28@6A>c{RY_A#4s5L7AT6g+j%pbMxjES)@Ar3iMXJs~Pr7(Sy8%VGzBI!G7pSTH zr25G~j*pL_nQ-|F&q$|iqyru@!U}vcj>J!=NhG^_dy68`X%zt}Oxa}T&OWi~FyLn) z_y;R-@GRsNMQX|T&V!T8XSPm6T}g3ULdVP)Ik$hS6gnO!g&Kw;)xR}#{8*^L%}+^^ zZUGnZge|v_?<$E7o77qWNlvLGuv~b0{qUK8D1VSAf9gfhN&RJq2aX=_)n5+A_;8?s zs76TQ%TV2LF8Vm~z%2$Ceivwsxj+*_)k&`gKe?!WG=QK!fDQY)uDLnL5;yzzxG@W^ zy1`r{0f2J|%7T-tp(aQ#p@t!RzPT-u_*ior_wk1IrFG?<+?+j3k_R{6h^17Ve#C-x z+5usSb0({CMqHk#PQ=2seG8%n@!k$&9#;{3c)+tKpY_OJK`5LU;zc zsF!{M7j}R0qG0}~kpSwbGK1aEE0Dh*vL=y*pWzwW)UM6OMZ-DaBWJ_m)1 z?`PpcXIj*y{)zhWV_eMANhMsE*52rcJTogWH!dvCA+?;mAcj|cu5(kTPyJD6kXJj! z+8pZnO69gEv3}tjWc1eJXdfNF_|u>%`jbf)S&Vf7 zP$>WkPPjCPO=|+)<-#=bAz4+4?1<~eQe%Pq&UcXV)BBClT!fPKH}XUvUhR%n+CaG} zbjTF^EaQ871Pa?rg zM`#I0822DRy+l-!{H~tj+K*D&a-?FCc9x)xQ~>xF)e=&;qum+SMugg6LNoEFgmz6g}oLWUvM41N+g{*rLN ziiZX~_!Xx~YGs&QbYbP2r~(OCCkX+2Ejb&hi+90&n0%aEEfHyVJaQ$z>wrVC@@80b zaMAhuBSa?00fx>@Ec@Cv(g28MB4j@ZBJd9ZZ`;u`c*y}4-59)-;1(Zk_Simw=RZ5sXm)%WRc4g7{S}@4m!e9ND8NhLbIbxWq63if@wt#J;H&!Xt4K-}@C%B<#z9bn zr1~?7!|AqAxLWu(ig3#rFxG!+PpF5FgL;>JIoFI~{TUGS_Q0vOy`&$14<`SOG-#W< zQun9wGD}$u>)7YTzXho#d-cdnWmH4kn;4%K8A}nt2DpAZxDjC~o*l4WFJ=1^bAii& z=t(<+z^}1^-ku!{s>r9GNTD_fHTsv&p=(48=S3GgK#tSImy_bc68GJiLv4;6#GvBN z$$~#fa)T7QLRaxrM=rdc37Pc)DjoO~Q0DU_GXZSz?kK z;YEe-HQrM#z>7Mmvx}+d0{&NYF*Y#Fv%?#gD<6>Hp^)IBKvDmVJ%gL!ezke3VZump z@VVA&`+}FolE1eQP|@*3;9T2otR(jPc^<&ymEY%zC6B%`EXXw0oO=KSCCm(E9m9Ep zAMOQfp8U0T4m14*42AS}7CL^OjeM7ES&@ZKp3lqt+&_G#lB|;FA)I@F$+^3joLA%3 z_jC6&Q;=5jYP9$A;GjiZ+80y{rZ+lPFM&3&oY3a-GLMUu2&EP7PzJnD=GsAt-|d3d zXt|Yz=bPku1lY=?cx2ZB67fO!#)NER3+Kvbv3aFB;!&|J{8pDOTz7p7drcRI0L1rv znb&aepf|zWbE^(IDXKr#Ep&WpLW}k}NHH)V)R8S#?nHOt{6CPF8$NOeVA8Vjlg)S8 zt=L1@V|$1s=zJVoY$$H&v?W#?ryW*NPg@Jejo4@P$L$HPYml6B14t1L!MQotycHd^ zxv^U&{tCM#6VX}rz^59l9|VJmRaTIgS8r(h97|R*ExLQOKGO3hZ4asdMzL}M>S)h= zFF-_^i=a6x04LoR0%8S}7hiZeyz72a3;fP3IKZF_IZ%ZH>adJStVYH+y84=WlctwS z1q}X|ELQV4vUSs7${s)t3_P6_h)l&EwQnydDbVA|vjAoO90{+xYiSMmSstL{hk6uA zrmF`bRmeN@b1H^R)KiYUMsEms=?Tvuv|VbRroPSV`QdKZ>P(}obR)oOvypEolzH+o z{*smtRb~K5R5z(T<4NuD(Udl*BT;|RLI|iq9%VTWpuw8QLy{)3$<~c;LNMK|wm|cN zKrMfi1m!-hzd99D!<%{vFnVN%gunti$Ui}#AOa1M2v&6(x-~#E(&H#yv5&B9n3;`U z1PD=3L+{-Xvc&T6#f+z!TxzGjBJ{N=vdM7)_%K!{6?F(`ySmg5YC^mn%pR?P>u`it z96ua~ElyP|P|F@qpi2Iu`Mb%CTLlXY=%*`MslY=r&|s&@JJdEDHb$Lk&FW82!8frG zK+<5`bT=Gul6;NZl@z^A0CDYS6@YEEMOHm>5w6T0p^)}K zJXW&cA@aFYpb`B69jWq>n2kXWKI4wKVQScj#-QP4m3ETI?NIu{4>`%WFz)dU=7s*x z3=J)eV`m~QXmK;p`nn-{`Mr zFJR(x)=hgUvz6_cRdMgd2ew#~`HEPVAsgpn4oEB9=st=gRu95W@r|@8@Q{Tqi8wLw zt$sbtYx+P6Wts!Sax0Je@ z89I=F6FN4;`s6pXgthM)O9<;gJ@GT{al`43d=X5#fZIfAw0c-72i8N~yn!#F4P??b z3e>?jDV<1sf_0E_Qm*^)ITx6}*zj&WrI?hb|#^Dl7JAY?Dx*o!fqTJxn2H!R)D83b^NwIvf?qIXkpx(r8` z_+S{o%c^{;zGJYfVPpe*vMkfu2Nlb2h&t7JB0;`Fvsej}2$KwKHXP@I#V)A5dJrb) z?7@Ezg@5jKCnytQQ1Jwm!+Mc6oMGah6iVm|B6%gZSmkoCe`C_QE zW^3zhW~ejg8n!bqE$|ssl56%L7`@F`a(p-)rT$HU?&pP8HN- z&<~a+g=iV(jEYYhPBCbiK=W`*2ZI@4Sml2&~1OE0UbPb0UIvK{r zzIvbnIZy~3^z|sYz^1-J8MtzTL~0BG^+pUlO?majAVHDyASEh!LPK~8_$^UsjNQ`- zE1i7ez7ZMYfFqrxI68z6M_sp`JVE0RU=Yew<2ECm{ZKQd=K=~~z^)!f%qaP_D87(8 zCr_#$fMS&FHM|}Uk|zikWwiA(N? z;kZr=328Kpszi|TZg;xn@vCd zA<`b|z}?>L4y2$tbZ2UAc<-;l|8148)?k-dDn3V7Ld8}3^$(_+$R*Ey1aHDMYY-7< z?%)?eeB1VbN!)XCK&d(Sh667H%kW~u%b+qVEs3p4-^^d^$m?n3QN8ZQYmh{3oO}a) zox|*r`;$}1oD0JlHXUV2GHl!y_|NbQdZYDnwc^`1AAngKI?AW&Ju400)dn_f;FtA+ zFsByKA!rV}1gm|;v`Xj?Z?vlwkROAr!aneXtAqIRrm6!O3-5|KFbCnb4afkWM&Q>Q zmjE|1LK#o03aV-G>W*s4pu<)93-w2zVC|#sMovvephF#BEJ%|J0FWGIRQE(YF%og`4w)K_Q|Q=&30;jVAA`6gCvI1%cVOEBF@qqeod}oe zOHWH-pl69kUgLz@%Hk{(v}K{I-O39~5CK&fn8#}t2J(2#sexR2b}I~Ii?85TD0O*d z6Yp{q6(mjyIO&;G7#JWmS)5~Q33Yn_wUArA#%Yr9ZJ@ZZFhSd0t(m9FR>AWUo@N4Y z9=W4l?H=m5G2rlI?k^uTO?<`TF~X)$qifJq-;lm-V=u(Z;a=ES^sDu#Bhy#ibU#hJ z89_yGEMy)j>n2raS@8Y4`g1lMzy6eEeq0t0kj4v|s6L>eomDk&Cr$DM>7Bsu^*CGd zXQm;8R=V~{Apr)CwA-+dz)d(`PF26b#1=s%A=55U0+XjNqPv{QC2zy|FxN7`6%k)) z&ul7Rgczr=o5zLCkuTSu>K?kxr`S|}Q;zsb^0e3#-NqFA@wl*FHCRLh2yQFyqgjpF z{dy~Ahl|ejs#!ACLnqb#gor#qPJrTEs~rI>{{mwoPAXsGmtcu(ADN~w}i58(GOx{0RCpFx`Vb2Pn<($9CMODO%9&h%ZB z{z+$gKBcQV(|1rh(3zf2>0+cgv5>-{r_4ki5edXI)H|zsGDHT{F_LaXQ50mx>)rgl zdUajuTxG~ch z+-MnVI4L@Itl=NJG$cH7Bn~$!T}SqhzE>&MnKfGPI@)HVVhn9vSLMW0T~|+3>F6WW z(HoAU!QbnekN?_ts$12|6`iv~Fnn~W|MtCF?{=xxb@00H)qCc9_4tr~>a4^GV0)KJ zKe&i*8x6XwKU&1*4$|rh!Lyy6b5gmvYv)*->r;9tTJbtj30*oxkFlx0@x2Z|z~=9| z8~Bu-`(7ndtYeh`Am0Y?zem?x{$A||uh-_gJXS64?*cyg5Z(9_kZ6OqD#i$kDUGcZ zOE01HZ@-s*D3<0x>6UV^n`dtX#43^n%XxOH}UbOg~HMtnZ~qb*A5>@>Hbt0Mi3* zJ&>UX()B=^9&qS^R6StF%I0Hp7#tzCxmpf3zCkCL6{nv>Q~voV^%~ZJL;wzpVMurs zhk6un!N-__ZJy>j8s*P!sH~4R!gx8VzQNbnAs>Mdq<(NZ%FiNkE^2gH42c11z_%E1 z(8-i3>Hy5F4!&+d{TJy9P~Qfqf5#{oVSxDZyP;#-5*qwtqyB~oKly|p%1|%EXAeXE z$4J(Fc-k$fZOAZe)?Z|x2vHr4BspJ4$Mb4WqXrM=02u1z^`z#(sx5>Aj4mObr&3sj zwuCcOZ4HboqzHkq&LA$JFV?x!5ejwaK8!(c6bIyreDx3GV;(%`;@eFXStt_JVKX60 zeYHuvvvcYj>$p>4^7US-W4!=L=;y0{(2?vpMjcG%9V9`F*YZvgG1dRh3~r-+}h*X zSf7{+(~tbl?H&k8?*6|UkdXcFAE{=};sUfly@bV);F6MFg=~U1L4cEd$ICD_Hnx2b zhxs0uPZc-AWsZtQ@!nS;mAPe49ja&V;A{Spga~`!W-69;Qzz*J`XQYZI)-C!7%C^= z!TMGT?dztcN_5m4760AI-%uyL3e*Vgs2)cIaW}5W!q3%{P0q3TDY~JJzDi5;WK;}I zq!4NdlK4+TQAI0M3OypxagoQl05~edGb>s$KpRP6_RtxI1h-ld1&i+i9?;+_jQH*N zx)~BiHf)C$G|K=_!GGg|+|(Xr!j2jm7tsRhZ!lN7H`|EF508=ous8Sd7BvHB^I-rR z*O5feF=*pyLZBB#JXsm3UI?XwlR)ejB3j5BMn@=q=iPb>ykO)) zF9dct7AS~x1ZItUSY=);DTjTnALFx(w{`k;Te$cqQNx)&2yjoS~Hh z81BtKYNnu%W|HsS+g?Mx;HG3DP)P232K*ymtR(gp+|*}`AK$Ip(1*z%AV|Wn*DkVK z;CTS?f%4!9rO$S^l%}cwq|L3QtOOF!k!)A5f)->(C_s&o~ALqkTo5W*C6c&vYX&ul0euvE|6}pbyYu z$9d%rW3PoJ9pPapDDR^-Zsqgc{33QHoiAv^dL0rD~$)A8;=^H<9;}hyg zslzZK3`zYqMq^v0>lZk1&njQ7y`9tt-JhhEpmUf=_|*3OJf=>+ zp5emy0qE-3)o?z7IfG2e`bAWpdg~g#!|n2i9tsP;$m8=RxtU&n)E(2)oBadT%V5|j zM;CJH!cI)Ir30Eppsmg054pCC8AoynlV|YZ5$yB4JQMDXXyx-M z+;F5yoC6{XzVF+^<3}N?7xh()N_zui1l~|T{tiCKtt#71pXu<*7iy-I zyM;D#x=`X*l)yXJI`Z$FKWDZCY4C}<7h6n25#Wrs4?VbMFJlY>{MQ1(`<C7>=%GB&y$F z=52J3ov@EQ_+VS{0yc=0{DqEsy{WI=Yh*JyH&w0Y30$5=3jWS5aq52FiaweD0rJv7Mi-tS z?wB0hnyQb5`zKsrYOhB2WlgRxqi|T)P;WfP9~Ft53Nod^B1D+!#Z66i>^qd&TL8yR7+R2q=1N1QsXa}_nBKe>uJL8m4Kh2Q~0aais({hMwT;ux&n3p%&Sya=G#+rB{? z)VBM=#Tkfnx!pe*G6erY3-Oid;E+wJas&Lz%5?R5lMJNJm&o%Nn!9&Qzg(R=Q+#>) zl{SQh2v}p&$LP)N$H$DO+f4OPQ$0XFxE7KE8vabo{ z79O7$YU-g;o`w2Rx=F#$I|0u?Kr&_mY}A8QD!GUxpf2+WX3$!$n}*pj{h9$CoUqK~ zqqWm)>g1P=&pn0qrG1yjfsy#{B_$TLZwKvTXDZv$RQfM9z$Y`Y-TKDUwDA(L9O|c2 zK!r}G)Nz9Q2!sMkx2aQcJazp{(5fCRMLy&d=>oe^2M|Lg-e-?~lx~K-5yJ!$+O4J3 zv%i~qpd7+hnPF=zKmnqh@RfvSIuY;Og32bFnpZ^K=A>%N+N~MX5^&lAHd%Sss{Ay_$^5@P6^pw0I-Kw3uJ0L$C$@ zTPwlr{C}f2|9Dl~z{wbLO5Tsy3dFIl9)*~8gyW`Gwg!@MVNq(|7thagA^TBT`#{f0 z$M@ApZ9yR2mmxjTZ&Rk*Cr@{jjv^_|;{f<6rMsFUwP1CL>UNAq`+unTvIlt(N{zuO zafp0Y=BI`?nB@EH`1*SsE_5_Ef}tV0=--gUWC(Eu2rtZ<{6`qguj&dB0sYQUu>5w<^b-2kEUyz z>Z&r-Pm@>M+NJ@!xcLJE)}u&vMRIt>=ibF<1DLWo)iV;u`3>TB?06xV8E42Guc-rH z(BS$OeO^&non$VsBOXJ-wH~pOm{f4GNvuLp0LW`~YAqx=(UY1FMG1%zD``C#HZyc6 zQkF-aQtyDVM@*mefl=xqB0#PWWCB#N@)^`ucVEx+|D7^_flO^1UF)E)4TZH(dF^LIKa>Fkw4tfGvP84SadzRh-lSAq|iXO9tmDof8mMd94-`1 zId)BAa+VXgh5Yr%?~@ov<>y0OkD=>wcJ)aD7Yd1}4g*vQT*EM%PH6BAb2=om`T8b> z+{e|R0+ROybUJ=pOXul0Q>-QP#ufCRdMS%&Zg>Ege&e&E2!03G(2Ji~$SD=BxTHd*4J zgK|}~(YQVIck~lGs^1gA2ODuu7+dQASN@U_5d^y_sh(ROnSoQQgojPd*88Gk8+Uc^ zZHtWAYSKvJHX-~Mf&90Jw!qBEZO_5&ovE%fv^bfk@Le{gELGW(gvDm2v+7-t`FycO z^;3Y(F-Y2dPh4o!-iV^n6`_dm7;7U;SXC#f_n@V}k(K~k7TS=(sK}7%WK{a_et!kZ zhWF>cH{y3r&6k_qd|%@GD%pR0AEhm|8NgcI;Xr%s;^Ur}_yhnWxe;*ID zIKUry5FSt!8{q!8A_St5GG&n92AdBt5cJtqTxI(mQqDn4@yF>3>dE3N+SWJ;(A zvxJh0@7Qr(1kOqFBFh#m{lMzVk7ZLWl+g5#hohN&7ZT5{TdR1pU5% zU(O@xqqm%~w#zNs=>xc5H_omB-x0Q3U;dDrPlOkZLmB*tB%0K$RcvbsMM0&zJ{ISrn4#X7!Q`SDH$VYFs zj_0kR(PQGuEI8;a6Wi;-yh!BEp!QEzjahzD<#!)`(NZN&7{FhIA?*S_HcN&JIG#^F} zWW;$9Z|B38(%O=!E$X3uI?H??h6Jv^F?lxDaHd`JpeW53o|T4`qcE#G9khgFkmJCB zc~FD!Rl2Y@jVbttuV|dXCU*G3{}*TP0vJ_!E&9)dnY;+>0SQC}88j$SfuNua5Y2>1 zn88U1Av{E?gghV;l9z4O51>AnAdl==4eey?w>^{sEMg(TY_J{ss2s?AjU!Q6n&Et-wS zy29won?7EsIL?AX-rpV+%T9kt;cxeKWpEgWxyl~ z{`>(>hUI)__fCa}B02i@AOR^W2?o$}Bn{_df#gw@C+FA7ne|<9c=W;?{Id#aPS)B# zrMoPL%>>9FpCYp)f4zr@3P#e`;tw(-Srj;5<;y8m&PiHzgM4vw@5gqjU!Qq%?_*M4 z>oGb1WV+4i73kayfJ6UTBsW8zdXE-yh4|r)N$4J>s`p8Nx;rMY*wsss(j}%hm+9@1 z=|$jj$n+L7z5f2`E$*LQzd5}x{$Hk-@lw+ps`nx3siyXJrq(eyweJj>TK_jq?LgbJ zX?tu+U~cX=Of9Nla%p2nh;hlALdRGeCx;GI(DO$4Du(dHAqGI`1aqQghA&Dd#(vZ= z%4=|2bDlzZBI!?(E$T?&l}FU^!V@gU_EX^zDW~-G9}oc_F$VT1`sOzB?_LHL<7)bX z4AIm?>?!2Dll2~!?{zyS62cCo@a8MD=MFt{wMuci_0+_ygPi?`(V_5W>-+Yo!>e~j z(sL+XUohLNpo91DQSjI)WU%?#_wa@QpIdz3Ug0ZJ4lJZ;vSSwN^OUQnUpOe$FUXFS zAgiy!P;GN|VkqUH@r;LzS)I~*ilr<$Xayu;6L*>*BIvE?G;Za6jUyOw>=6MEY<293 z0d?l>p?{3;7W%ox6+;voT^{;IeD_MP`L{fSre#ahzW=V?H z3lkCLBNQVU{^-0UrH_m0JdnWD!;?`I^8%DH_>=9ucS!EJ&XJ5scs{%9(;f=vbx~`; ztWTAZc5xZj#`n_h5wA&leb&=By_cG0?d01ON2R>)Tjf1?PF9zGCJ;@V>Cb|v6rb4S zq3}R-rb9Ilu&)|uw;EVL=^lS;Su%hpXOUUUYN_S-6eO)mK_}NM2?5w|Oi>`&4%o;f zUjF!;VSQRAMm8oRCIob9RBQbBS-<^)^zy-S`~j9C0t;Cj%yJOKO-ND5h7UwGauA#g zkKU~yMfaqfU7%G8QUvr(90ZF@NFnJ+pB?a=4UG&aNU@2DU&KK`)Re;L3U*;UJOU4p zA|9GXh?_ct`rD{(4RKs3~jUqFj`vp>P8jvy2dVZoz zR`b|z8f;KnnLAP4_d3%T1hM)0`cji(+=di_;`@PWo0@@_Tl;qRk)^2JGYvq3y3P1h zSe+>Pn|zG5sMs1mWj%R?5-)@+?ux(g1PbWyC(OJvNYmQGcml}fqMOMdy-T#nhLA{R zOpoyCkB(0YM%IH|8Hk{P9c%6RDm@D((E8W2Jmk5KH@P=IDYVo5@`%qrcfy62*$0=i zem>{JT1u~2pS?g@4u$y~3QIT?y5vv*Z?=vMIrHc9Oyf2IxN_#tXAV1n8qwRBh&=&B zav7v3Cv2pIB!}e8=iG>LWZ>br{}(OhXPGK6<8Sl5;#QmNQ|cn&~_>)4+e)#Y~e~>X(@Y1t6nRE+RK+F5?~m zuP7qSFz1L2m&;9rt5E;apEc`J_EaIOI$-bXJ3V&pb3KPIRVgPW$0VFh5GSA z{iWVz+^N6-)Ie0_M++Kqt5*IgstR9zk(NSX_T%t~L#kD}+*+lqg)?fEaTd*S?V0jp_B z!o2$hNgQ$BAZ4)>S#aqimt+yf$XX1z-g<+5HPz8HB zo1FoEz9)j%vIal(XT8h0#Ea;64G}gIOkG+15ytvD8t>2g7M-58=72Omr%{}&36HX- z$!I(cC^r66?C#jR!V~G<9TM+x&cx9kNR;f|&iQ!wf?Wd}X5}3y3bo#y%Qf<1hU|~B zrx~*Gr{nQH=W=`rXiM4pl|gXRwY_WEj27jSOB{pu!R|jZO3>=YK^_hhaHw~)iyHwq z8T_qZjctM|`PMjp>jx(zIX~J@7z}xi6aDOAdx$!_liSz@*os-*CPNWIJN zITaqR{r*)wXARk4IO@w-at*G|_lM~RFs7>?_UkJ+;eyGW*7Km881`IJ20TZ!Ry|P- zUmK*CNx~gIs=t2tBVRP_CZGPwnRk6DuS`0rcb<83(mw6keX91ER_z6tne)4Xo>#&n z4<9>oXwn;c&inhM0RjCUPPQUfbe@b69pL~zFH;6y(L82oi)PcJU(y12UrYuEz%W1x z9k7UlC4A1r_*X}0aS)}Nip2CqMtqxESc#8_!w5y^rFgMo5}o&zAdva(2g6!^3Rn5( zeENuDX%IN%A~vAljwW?w;*zuGN&)(WAWm;4t)OU8x-KdO+wOs{ig4nYBR=VP2W+B9 zn!@z3!bhv%oP2avx)fO~(3dB?k%bKL`-HtI=ED-zjY1DVIQAFPC~(b(kOKf+F===a zIRFJEDf$4;}1&*VpMX0@t4tb&WR#G?A9@M^!Ge_Q`(0U)Id?31h;X;pf z#IsAreEKzFphO+A`KL#AW+a!{nG}0%EF%2Mr2S=|2|Id2BFEfezk?2sy!(0%6$>%YN^25N<7%t z+Wv!ZPIS4OK$oZg%)5Fwjk;#H&j&Bo!fKyCy>56B^o z9+2WGsAatsBi@dM5yw7>bqX~(EXXdP0u34gRiUvR9Qr)jCs@{zKczFgNYQLOia+X` z#QRsR?M24^%#lew)(W*~k&YNDnZ9v3!XuCs!-kLHL5g`BIYLR1x89*{kqm# zC-cgRMFY+^tXOcBe@MXoKk+97lV!pBXDh~p$D&?me2p<7B=No9uweK3bZ6*!S+IW3 z0v0SgoCV9T+C~4}f^ELkZ$B;YrXSd}1~Y&{nQ9fwNouX){c07nL)h(~1BPxGO1>7D z*i9toWEb@7OxE#HSkr$O_~UEumURpR`g1+!AkhMTide(T2G%jaIYSX4Sepp2B7J%{ z>ok3ORu2ntXv-z{DU=m`(qRSzY{vHcG z5Os{fT9S7EPb3F4N9*_b2zoxyTK`EtxJADYj6%e(xL^r=TSg$7E9$F+s5t^Rf=^K5 zRN+};-BJff8c{$a{L#%v;WQ+JAY#Hp3g_b_u1%^3I2~wAVrVWWDMoEdX$rP(mWrcK zF2KKj3aId4`v zwOyfd@eB*798DX`yH%|%q{2*W@&j0jx#W;O=G@$$wJ17fdeGz2+P_QEh#!@V`_ZA1 zthGn*x4duPDdht=@xg@&+SjoHXZxcIxE}k^9B(m6qCX$h55YjXCJ=3Kp>tj);@#Gd zG!9Jo7NZroVRx~wv)=1pVgXUjvUM0e8Vj4Y?SfPa)Ki1 z+y4hpKrVu~2WG`4gi^bm&+{RF0Sp0X?V}SJ6Y(l|@4&~EO1!7{yi*Er-YP`{gqZAZ zH4xJzAmV(LLUuj@_lvRlHs6>5yAP+^c|X7OT{tBZNva+U=uS7EI|K~+by%c21KipH zg1RSsA`lHf8Jo*M_H{CdIyg9ksI|55AsxQInUA2So24Wl_K!i||9zL#2P7>{4sw}v z0kpYyN`f>?O&l_PBvtbejEu&7ZbM3NSV^*cTmp#9saI5Y77?Tf>?J zlXBWq7!8j8X&{>GhQ5#%7HWC>jPFyC=5qd1Cu7&`O}>%rfvDHz)kb+;nrlQG;d}?& z49Pma8Io$8Zf708b{P`gxtdS%%=UNN)P8F1NDiRu7X=^tw6+Qo$<3s&rh>5Bp!hgq zg#QdS8_;vXn`Zd8Ud$+c5Xr)CWR%ttLAOjsX^;^Y$SP2lf--u~spkCO8Kr0b8zOrQ zrF-?<*-GY#GoU+H9WL$XVRwm%KmPP?VUhw?z7jq|P&ZvY{mo|?0zDTdDU`N~`~Slv z<#H4nn=3&`h}avH4hmJAT}a>fXVKX78hRs7dMx;21}#(P3j1O;jqrO8YpsXnLNVf8 zCw>Lz3*@UG&MV+P;x-8qSPz`4=@rrh6^hNFz4q;)moV$dXCWr8*BHi&yZuyxFm=q3wTvr=lU|Hh_}R1yC}6Rpa&u4N(Mk29uDyQzS1UIcVS)bfUE? z`NH6MGQ#ubm?)Q^L$>AtlM!EKLKY;LsiE`~#DA%rul>JR8*B1jQ z*WN*tYvw)r=b&8iXIS+OvrT$bNY4S3s|NwqL6qxMKgy*zaDvs!4guxbMF9%sDgotE zfl$Vw;RiqSDvZmU1;U9=jQ#$@9}Qw%uL{=nxgP~y|8H2=+vw5xhgjE66097QU)bSh zDB!BQxmnQi&LYZy;#HA1YCjv&o>P&Y6HQJutH=8M-5o8ETv!=^2i}Y>&c$Snq^Ig~ z3b7oXHOsm%wrM!l0%2+#J;_msobu!h!I{TJVx0cQmemS;?G#)Sd+g4}md1mH}-1os_y+yef&_%5P}=hVeF z_Zf-x- z*k8_9|0n|{Q5*$wXcY08&|4&EUgTcuZs}ziRZn^sX^}!7=KPt;H0$@ufw}Dbo_xun z<(c&_DsWjcqF&w3c0BUO=bHY{xz1KTwFhF12t=xDC&8H5rfh#VW&XhyuaSJ>s>+tp;nDY0fIdWsk z?WJ`YelJswgn=pRfD)o1Q+_W~zOaAF@9m#*;Z8B-++_QbZ?`_490%>vqbSwiWPf-i604##UGBnTc8Fvd#}4XJZ!Y2O~K%C|zGR z^e((W;dn4I)LnRiTZWgnfS`G=4&H^&H1EQ5PH_Q%%^GzVZr+cB=ADqMuC)&6E4@dVI~e zz!`lr?1KUm&5MutDUE>QKK6l7+%K|#6&#g)B!72wb#XZ;a!vwDu9I67j?457pU`R|TFjx75nk0VELGOG80s1E#gP%W+|RY^ z{sdz@yH%QiT<^GkdNecw&3}xNvK#FIQ2fjv4WI&!3dV=&fIf z(?la<$?;2KCyYPPJX*KI$3iXbb9QbPQRs=rngz0OpYL-%NkDSwR^f^i_@Zm#-bv5L zW*A>aMIKt-M$cez@P7QqJfUJL-MVu-85q8S2D(sdnT&E}+y2&0m^AWqO zenr1%VrT)@IVSR|i`8^A-V*>zV9Mde$HUyyhZ)a40b;D=UIhEHVnO>0qDg@Z);CZ8 zvQVFy`J}xNMe(tb##4TJ>onfQQF59CPrG*7-5?sT(m>=FCnAwJvCkQx7miS7C3hv0 zSJUW+^bCL#RJ_fFx>}H4V>&Xruk)`{9v?vMKia`@pgp!{QqK@$(->|uj@^PhWH&jD z5AvAAQ!E0bT-LO7VQOyg@|N^Dd+w77zof6z6&h*eq^Qk+b!FBRyEB(8pXcjm zu|D{jvebtvjA*G7QRe(eoTnDMXUZ?XUaC%Y-23dS`Iv|vlF$pyQEMhBQIJ!xoFoO z3=RgNfI?oEaano)O%fWJb@{!L9^Q}qVSjLMbD@9z)aPk_HptD(xJuKO=jG7l2!EtH zyw^EWVN+-GM~jrxV)VS@KQ&WG4aScZ+J>A-C|lxu4k zt>CQk<-x!0WL+Zfi6$yrpQcH zq>TYg@6n-1s^|0jQdMJoo`ahwMM@l=Qv(5qHoH8pXpePxJ%_ZP?9rY+G^qzAl25U1 z3MZW_^qgY_xc6+s1Odv+ZuWt_yaPby4IhuKQCwWb)7M<>`+M~RUL>XMuV~x;cLIya z)2H1pvSTpL;=%UM!cIZKo$8j>d*%(_RU%%`B`g|-TKBv99GR|Aen3Aj?d@}3v5Cc@ zwaq0vCyEq@e^L)0!f3B2_s|@L~vmzwWIY>{677?86BSP zrt$h-`-Ow=qR5QJkUzIHOBO?cJ@%n~CU(?cZUoEE26%yaceEkywV&EJ(d#+YaGCzb znfJVt&U>Sq<39Tv^{JBAJ9l2zea-qC@}9dke&#*@51Ez>mAxx;U2y%`>;;ih&!16w zWv?GcQT3yJS26ZHjIf`mPnWg>U-8S3&=)AG!_#GFPK4%7Ri^teP*Dy#k)ga|Q^t2oo%J{J> zx8*|0u6X?TbY8(7vm#2Gksxcvt6*`xdz`}qL_L}I<5qYi7uUxHX6Sek4 zis5lh`F?-AR1pzdh>&Fy2gf7%l z6ga4tQ5!kS)hlUPj3@@^5RGWB#(Qh1gUslHO0#d>KRTD<;lbMwYuQAwS=cUFDnycW zVf5LlHr`)J$!W$_6iQL3jSf8(8F3X&96*~+s7x48+&|j*l9NmeqNo% z9bAw6J6`iQI-`8(S1CN)-$z zz3nBoABL^cY@s>%AmCh0EM<+$AG)d3ZsH}5alJ<_En%t5%Y4cna>-eD1IYwMI=wx1 zvfJslGKo+WIQ$T~DDSeT5c*VaI6$+Os`od2oId?|cL}#1rWJ(!~Rq@w%bx z0CxJ}Bl;PwD9;f~^Ln1&;*BN9q#W;a9&HqS2V1#E-8??w-&w5w9ml=k5}gBf zu8tZAiF5UWCBH@J7{6dl7$Ix!h2DDse^>z*Z2l5@)9u`MuS|u>ZjyKVT_{5o^NexV zGcP=qYvZUwowh=pdjB^odEkLfLi}i#AF)2UzJOfgSw2#n`)7PAPhrk-wGD5lnfFpC zJUtYf7(XB;KEba}Jp;OhBH}(2GtSEn+V`IRkJzy;&ZX~Uz%G-wT&EjQA%u4xG6Rb|RW0Tw$NJ zM-^GKOGeqa0|Y7nc?$`VYCH%v*xN3hi!{0-m&haX)2w_X{*sq6DM675Ym^B~mM-ElF?1e)g6#!YBfC@=;Q9guf%YC>B-!if)&55T5nj)criuD{ zK704U_c#~}DJySo&1l&IciCg_d-b!izv%DzQ5Qeb>v;#k7!DK9^>a4Y#b5M!V&Qf9 zp7*t=KvB;+$L71lO5qt}2E9?>>9~w+`P%{RJ`|P{e*taL*}kl!-uO%0qh8AU<4d!L z}O12??>JnN{yXc1SI#(RnZ>i*ttu zLC45g?4p-V6JyhGnzQ$>>cLuO)Ti5 z$%8TQf-}f-YnR$X=<2-F5&gE^=Q*Rb|4K&MQ^YmxPxf){k`jM z2}WvNqDvaHZk@+n0W^9x$=P&BuXS*jO$4Dq`w6jfpl?P@@GY%1T`itN*-V|iXYLfO z-5~)UKQ*0K<>yk#QNc&=k3PZ zP;SoBrAN`QeQq}D&?z`P%je0>2v7HUHjWQx(Y~xRgKhTDy)ScP_!3_h23WKEr`uld zifzE+7Pa|6M#l?MDwx0+^KIPEs1=1Z!bw;tte<*Wgn?nJ7)K@|Sulk$KK(|eOkS{8 zwyayyZA5dqU%w4mpZOH`ZO-xeF5kA>rfgf1B_qU<#ts{Lq?vkb)q6z!^kTNhmTQCM zANYAp9`7n#|6%sR@_E4f<1Sj5x2riLC>WWg{`$Nu=<~E;TAZx`lfQJW7Lm3-|$$b;c%>F^#Q+ z6^F|-&Y@#;a>Uyq(x&jJ-nn1CX7u>W(-%+@%Cq$Jy=G9L^X#pG8TbaXp6@F9OIyATQ0FGO-OC~n67UU_t}l)kCQ@Pa99 z1BL4EArDmLf%0$3r!@Bb_-5d;tYk6NoqZt6dtZJiZDg&YEf_`X9zTZ zPb?!^{FNv`5PNt@M#}e?8K?1|H&I>A4;~{4_HE2Ztq)J7ED*_V=d&9nFKuj948@X& zvu`~}Wg=I^C57c3-o?geH?nu{SP9!X9&0l;kJ4XN2=M+9 zF4{14-I0vx7U4@K8r3wQtIzou_CtGlU-JZ<|4? z<^ZN;N`Hw*ru5pKY8Wu#r!=y~(c9c1n=uzf`C9ug)i|S4N!CVWLX_(Jx)Q>l^mRFy zYEi*rl-(diX86s%u3;f%JSsQV+}D+Ce%Hs=_jS49I(50sfYq^;ER+v&L%cC<%WR?4 zSb!GhqCv(#cj^cl)i{&$pB7F{mKMS-6HVCPaXsTEtEN0F&-R*UvxRCKiIQUXj&EV_dBV{HzF*4goN_ewz>=rs^*3WM(kqiq_4 zcH2@HW|J_DRO*@zR=6j2ToVl(|HX{?cu|BqdQMofWR960PQzMr=4kR|N%{5e|_`<3fxkJcto9dPh7eEtYprQZ(os?GV&;*I_x(?w1{s*X@n1%U60 zO^KvWp+k|J8~7E=fbSahGt>kei1{%Fq-_&*5VZColy;qL_;jaO22|gT8qvee$Zh_3 z?BhuKyHpa%G59r0y4oyh{X>`Rkj9F!U{A~WY2nnCZ(|4uHPu}SL%K4FU9nTr$!ff| zWe)Al!+sBtFm`Hh!t{rRQy*{U(QUBxd@cqM8i#LN$WaJB+!H&tmtm0o{fyYIQKjs9 zot9z?;&z$?T6i~Yq2Mi-4@vP?QV_MsjM|*Cw?(SA#g;R_t3!)h(GJTEbF$eeg#fYb z(TmLG6T@fnh(($6r5y_8?qG_>gAOaJG08GVtw+zbcBGLw!qs+ej(~aZ(<=D`5z6j0I2)?FZ++IA4c&I?`5m-%chvrkXQcIWt?xMyj z-a}1kY>Ut}tvQ)LARYHhbEh7zcTyW+av!aXvsKn(vh%6waL_ zB($2q z=Tx)pVYkemtQd4~$OtE!yhGJ?F9&FGpR=z*rZ5#5CSwe<63YRgG8|&^@!5OK;ZVo% zf8xQMEXUz8<}=299wiSg)%fB87Me_bYup)jyo8Mr?w4q!t2Le+zKlr_y3`b%kWu^5 znRCogTC5uVH>wR8hM2sj1egAqYNse%%a)VdnfiU3ZMZX8{U-9uO;bD}l&0==TfZ6> za>S<1h@<|d{1*zYU$&b9Z6>H+`zsLsxN-hL!TYhBH1j%IKRPn&LD>yb7uQK`M9TNK zAcW6?on1n9M1jzY2lG)HZCx^1Hs8gHhqt7EvxKkgbN;Se2JuGk?Lp+1thu$6^!0vr zJqvb!9;7jV4!(dW0Nkm(;k~g-y(_*U%ddV23y6(m715w<@`mMKN$GhF%{h# z^LL7a>59imP5+fWsAsOg!HiRjE!W7fA-bK9Qix>XQ|Hq~O6;dt)xjrN217oC z*Sj=We!^P{YiH?_LeJl|`vp1ZSK<*KVXS4Ixhm`h8Un|s2FSv*6Dc`--R>4}WMO)| zK-FO^5^`fyyG!C!Ghu_#DBYP(o}gIVe$Q`zI{^6$Q#W3$>W~{c$sFSR_-yw*&*gGv z8%b_3=k>23CC9bgXI#^X*2o~DVe9u#IsHq7PM@!pZcq3l&QP<=7S7*9a6d+DjNu^Y zc8Z2b`CHkw*pnmiT47Ed4o7mma?auc-*7tRuO@y-%aqtR+7s>W6 zW4EM)B%vzgbmE0Fhk)@N=BUs4)Lk}PqCY}dh@;^c8~k%HZp;@j(a#(2 z=|Jql>GcZ9k?QIc=a~pMb95zzKRf8Zq9ykHcn!5IU`% z#PF5sNQi7whmF$w+kfC#XJzb;$cSy!E2LkN(RU3s2Pmi^$rzT!cMc?=Dnbsb5ax`| zPBE7xr_8XNo2kVZPeWqq{h`L&_cOAI#%1K@G?bI)RwqM|NGjt=#-j_JrXXvQzq04r;%c~#80?8*&xWmE#htHho)8Bf?k^Grhr2I2RG{rItSNpL} zWeCq=7^tNH!$3z-;k!(AZi*1#&kKuRhz$fDkwYH|x{K+!mr;uqj>RU3poe{!NJH|X zU^X2$wQ~JCgAq=K`yKurvJiv!)aYB?5s%wBcHpW_?`KOPFZvUC1>n`(@A$WVAeS8h zJF31u$!K}sCCFO1DzIDB)!ABDd@#b_X`Dyr)s>6rpDhToIDmmy^x5p8OM{-tk46EQ z+Wg6n;1))Ie!W0%@@;)VK4QC}81E&9M=|3eY7ki!;k9)VvYR@F)UwV$P_0p%ACGgh zJK3gQBg$pZK%D4!-E*5PP{r+-CYJ_TGde_d<1q9G{OJFART{2d7SjDwa`bQmJKLH3^J&w4(92$JzHv`&nfL$ZHx z646+Be^4mfJeEff~H0egWQJOB@1U7LkK?~%K3CGSf ze`M+7G3QQc0FTvo2yfr7!>1SZ0mmxc@QDD!D#A9We{TGRv4|A@!5^8+ zl*;~0Hs0lE0m6A?I-`|GQ2%b~P8LP75C96a1J^-&uM$jpuJZ*54q;v?Ou7S)VqO`X zi?&$B05Oo;PYej#lo!C67LemDMrJ=zFupZMi!dc(()x9w1YA%8lZ+lI=`uwC^f{Yu zHvw_&aMjFhKVXA!0;r=*uG%aGV!|v;Rl?$Z;kmIJ`Z1>vBAtCtlu-;u`{EeZBRYshNRJb=Esu6@0q9dJ0#*U`VyY5y4e8SkoY{vlQC!T-{V_Yt+ z8e=@f7_!F0*TvXKCyXsLQc1bJ9S+L$j32nNVz0uc1DM5lyzmK!N%*=yncCdm4o#8+ z^U!57y+~lz+p%ch-htNlqf9CZ55%646Nmk6_8oBh6}{o!i(&91u3=hWGD1yCs7w_1 z{*kdYWyTgt=kBV48x6vLYbccB!X(1mYg`|O+i1D<2^?s6-*tJy_)g%V!tQW zc%$Zi7dUSJED#+teh{1*O$8=AXG_@FMsdwP+rPC-!R~$V$xH}oQpntB=r1m3G zw%QN01r8(`mq^=P-sfVQaz(}izT#C@KTAYE_y7F68frP&@^)p=JVkJMcg z7LgokR}PQJ0_A^JcE)s~0#m_wc>>f0?8<+WB>zehJwuTpvcZiAe%a3b)uKb+ z_=F4+M!^&x%GbLUl1fqc6CWwkKLiU&gpf-r50A5>eA+i1VDKASinO^oQA2Mg)r=yob znT01Pb|PC%K`LeN*RT0{jHxsyHeDnZ6CyVW>S$a}h|uQJl^M!{o5C6u1T%gO?B@o6 zD8Xq%$ZW&N^}{R%*5ByYzb&%5?mOhfeOd5M<6V$85$a2qEZ~MFNB$*FIXUNL%YO?b zbK|$$4{dA)!CX*AaudP4(n2uj5hMsEx=uhY!Q??MRUhIaYW)puX{k}>i$*pRyUx( zBsBqo1@&cqD0x+8@u}aivBxJU<+9btFmP>)6yOsKG%LDL$jrAxa~|?t5uZ0bbi=$= zp=nP}?{jWn0+bJNDfQMaFcTo)8RObCI*K^3NCO{k?O5gA=C}fK9dzpo4E6@~_k;Ei z23`nl;g1#qN8UUA^T@2bDNf4x3fvQx`;&HgYjv$T)R7VQ5ew$RtKv{fpnNI( zfrFvpMkn(SA$7i>Fl?m34Mfjb!xszE{+apvyYw9LI1Luks?62-9PulWD&IAo9vJI6 z83gvHg9liBI$o$>C20;!gdmSGj!4_f;f3h0%0G5KwvO21acy#iBbx8DtUsegXzH@mmVmuUT6 z43$`a&k^mB5xLuLRvRYYUEUQ^-A|>*BI3Z^K(fvI`#GVjcFmQ3!zg%!+8GuvW^28z zU2|e7-rav-#7M+-_H9weIN^UL&wt1<&Nur==o02mEA;%i$(gs!mu|yYd+crBwpE!E zsh{fU2Kck|-CLqs5rm#_8y_R5wDR^grDBT0d=HVqHzy-}Ne~LrwJ3VuID71SYIu~T zP$n0mdFZ-AHv9E{@mf(+*>pq1r+t|-Eq_J6*7mRTBxhRX#!9ViyZS7z+n}|ztIzq1 z3PYR8kvrBHyI;i)t9Q=jGlQ zV?nU%!&8eX!TU@O1GU6RB+ts*DI4N^=QL`Z=F3~a?Ue>^rYAf??Gn`?k=WTejn+-8 zShH4kd%LJWioI(}5-aQk_5eUa?*CGaYEYcsC@V#vhnVGIMF#Yj(Na(3PbIRJNYNDS zIoOnHY?csZJVie(1VWlI?ot8T-&zF7;JAU?^F^@&VZ~?oFm1dMv5D&j_9mU{j+4W-NIe{quVOLYZPOD)3fSVEDI;KPG-u zcH%`4jn%5MzaUrjtnl6lbM}wwd6a~*jrO9Eg@K=~gj+r}f4w;{bWW%Md9tK%Qmlz&bjCaAMW|D2?xPo+#w34QpZ) zv;9+bxTv%7AcvitTS%0{KtEwDO;8lz*5@S`6vQ$ljZkd-_g&=|7+q7lF=ci*sW@F8 zZANnTQnE2zo^I&RGmxSypiS@vr55|RaThfRE*mB&GNGEpUj1Ef>ql}Qifv6>EJf}^ zhs$Spma?@dccPccooE_h!GS5l-kId>>&n&Iu9I?Au3kyylvJF@0zG1yN|B;cuw}rD zzoHL9&OVaaLFld<@QR^`=f;>V`T&0@iOh;_^j1HO>a=>@XBu;B@A+5ALX-RmS*~AB=dROm#EIlxIndE+8$931Q zZ<8l5;3x~+&j@MY|B?R;kE|EC`}vDw^?eEx6ei>0WAGVM#&ioe@(vn-gd59ZDyu8v ztfV0D$EliY(K)5d}Q{?<8xk<$i6{@{Rtv3sz{~g+XM~Tz9R|M%}fs3u>*& zU!JJBs@$5ZHd}Kw<@$B*w+qO$xmX*u)-O+DF{hBIjjCHA+D^r^f;Oso9qL|52Iq0h zMuNvj3PSpVOn-R+yJf)?VpiiKyi-t*3ot5JhnpKIAn}9S7@)2v+*(@;AF$6fR}nQY z%hg)K1EgXa@PMksY=flD z^+z6%1`M>r6{@G14$W0S18NxtuEVTGjH*)&P~B`+_0!l<3#{75@M+dwP+|5RL`_vq zFI3-U45aUpmA*3sR^M|&_o;qwrr%O+k=1Y1SS+L8TdZp7>H@P`=3~^NbpyR-E@jOB zKlJ+IP74nPshu?e*`&ff8oa{|2=c};sRXR#sC02srg3BWU*$>UD)%$L@F7eo61mEq z(tnjJA|?k_q_a0~6lc}{x(8S$oC#PG>oHV;c9u~2kh9Joz5Rk$+cj^zA>wG9NtX*s z91xBB20v!hd727hF@dP?$FWQT?;FoE?e%fvsEQG0M@%CB8q+YyJD#Uf-e(2K#uB~H ze!=AIx@Zej*!$!b8tlxZt00Ts zt@~{FTaE#DM+9G5rIB%xZ!9`5Ct5%Jts)b6d)R(DisW&Eq(2be#vHBHuBNmW{?vr2noA3~35 zpu1=)1GJFI-}*6(wr*GKHNXA5-*aBhu(Mn^<<8CypA>wJViD_1Y(Z(xB@c~BL_u-V zwlPUY4Lt-$~d$l(eC}Ud%sNz)26(Mb3kfXCT;II{PE#qls;aYf*wKIY*zZBN^7Qw z9pw(Jzi+v1eW9e5*|xCsLSDGvrahgcJv|mBczO>U_gm!NWXAc{V@cteC>2f$Ply#G zPvS<@$xb)Nd0USrg>KQFP8%oVJ=Qn7Gj!AoBAC^w?~}ovaY%y3#Ofr&&}89dkOARd z?di_0z|Qyu^DppBi}e>n%4H8{P#VbhKJ@O5^52L$q%%FiNgcsS`}`^ULhqfHPGFyz z2SrppPJ4RH*sPu|3iM@lba6W=y717U)PhPW?V+@>+tOS#e5MEx8RmqH9h?xRTulf= zh6vXO1Id|;J+aSO0LXBFPon4}(@2q{MLdEXD^zGt@0IKVc`u|N28)t=61_7J@4({p zeeHvcxwIG-~6WK;wb^M8qB$$*a z8;&lu2kkFwPv_fmb`z?7SBL{qLTJnRf}br+xKDrk%t7tx{n~T;4}YM)8Q+f@vPqrZ z=Qx|KL@vE6Uw`e)VeRR+wddaUX1$|o@M*h36KwhV-||^Yv%FhB>L7!6>xCAa@MKg> z-P-wnqAvxJ>c2pg`=X8Ue9zy*`$ZPqzDM6j_m=COIU-cvG&}l35T^JuU*Xvbf~vtj zpDbMT&M#7(w!iS^JYqY7@2D%KGd8>`PY*7*z?-koNVW^(jl!AckL3ZW{#tJ;m<7t3 z8k4H>x*ZEZFG$ia1mNHKT;9EZeeIFmTmqj(pp!=fM3 zB_Jcd{o$;vZAyp z2dlL3XoB~|VzO)xo)yT_ArFPJ&iW4mV!qxa+RTSbB(#+0DO)kzY;d(t*=r zuH3CX_i7}q@DxBCW9KITK)0r~B$M-K@`e53Pm&FP3Lp<_M!l7)t)B}ecEQzrEx$=k zJOECDw7ppX+=XFHNqQ2VFU7by04~0Nn?nl=z&!m+*!w^O(WwA1fWaS6e|834hdBiz zP9L2{7Tgo_nXxiX@|1m$^}1N@$AgW7x9P~1<=2s&6A5#y&ROzZW&b<pL>^;izjB(grq;z8>z=dUv;Thm{L!F3OUUMQ^C}jMU11AQK>G@>*pi#G}8ZcWTe> zKYUEzkM3An@+r5w>p#!%duncWhu=ZUA!E`wza8^Li+R6(DD4Mj9yZ{3r}eeLP};b>hh`?_Zkss{5RtbnEh&I$(+|7a`!*%-O`3Y`w zv|VX8x9yiq**RemLd&)TL%jdyZ9RKw^RU)lH{i#SogeiRa_-V*hqu*mdp-N4%)K!* zSy1LvU9CBXXrC>d6mhPhiCx9UC4#o7x~9k*kE_(FH!^D<#=rJ+nrL#GjpQIfN|4KI z&w=pAa+(1RJG72$H`Q!u18Omn*jt!WuKJTbV*Uo}8Qc)rxY&tY^1Mujgd) zZSHDx1q$X1^pbu3$uwq+eJxi~Tv9oWVxT|xArvw?fQG z%McF&)T1Uq4T0)_*_>CH4FI*n!fI5o0;q?Cm#u*5K(AQV{k?j?su5_VI`wU6N)uXb z_s2(M)Y8R>bChyMT!yo8ZUC!Ai;!@@&OtJE43Iyl&w$DCSJg2+pSfNNMz$rN2R%Uc zy8NTu*PisFrDA{A--O}90_n9kjLe-K8VCA(Abe6b7At5VrpgmqexW~R5t9x*ufXwL zICZX|z7|MT=ML)4Q-b=IY$51{aAFN^-`?l^r+LhT)_xojfIjCIJm`M>?Q_ET{S(XE z7jYUJY_>c5oE1cE3{Tda%W~|7m2j{ONsAu zP9N#xva_k`WqXtYOUoQjX9H z9fS-UT;uwjKZ08;QZ_$k?qD~ZSO(+Zhe%>AH*XlAp~~MC!y*S@*ugg1M%9tD$3$=n zY<|>?4piK=ohHz$U-+Fpb|d;tkI)RQ?H$69`g>b>dhoPHp!AVDhlg{~5Izz*|9V(pC*tso~ zBY(m6$vT;k&(@DW(?Wn8w;~b6km}cG#vDjK9pbeOQd@e+P{%Q1JvxlCGtOg~J$9Z#wVVXFyF-=z!5(|X>yWqNMQamNlf zrH8Y3fRx(+t5LEW?R>~=| zBr@R-3}2t~h1oI*um}Y7UTXD)3kkLXvVg`)s?$;Nyf1!7mrxL4@R2zd>{fnfc6!5=vsx~X zPj)GPV%dcIue{bhAwJor{3*q0hbFvGoED#a@@C0De7oaeM_{`nKKZEf+dm(F!$hx_ z^gYTya{HLP^z7o%@yQ*^pHpvZunl*O$6lNAznPnsFsgiYMtt()%0FziZH+B)wJkn* zyYi2iG~wYZJnxgfMfu-38NYQBxPN?dz4E8cnDDbJ=6p{4YUTI1ZCBb7+~hA-{zWI_ zbz`!(Qh%=UUz2OkvyaKO(|_f^r8uD^;ojl|`mg-S^=Zc@bl1~Am+~k4ZbHJuTMkmb zP5DzFcl^w;@NxQkQr;EqNGP_I*qp`GcU1XDZBKb>!moQ$>A&*ZvTZlmhGi4qq5Q+Y zHz99g-eSh*N##!-HKAnU{GU?(de{^|83gO(hfYHrcLzOIE3A|Z~T;q>`g+#zYB48mAu#gB?NMuWP zeC_P*Rca9x=9hYdIWuf)!qqhu)7RA1g&IQ*W%bj`>*aFKHnqVv$IZuvvf2uFRatdS zWksrO+S+^nOZv6|4B@+Z@nm54*EMdaTVUYC3nambX0I zpn|Iy4O>6JonBGdG(8mBWK$ot;hGxrt1(njT|4ku7Y^BqmIiI}7ZnAHGR0Z6v^3Ku z7rwUXp$+x6>EXtP>1(QMrs%=dfzUhp#jc7Hn$WT<;<^yOe zZDqC)ze{YJY#RWqb+#H?wXNJ1PSA993gYU3poJRA+km zuoa_`)mF7#w~cm>wU2iH8~^GX%j&DARnWDviY7j9yg_-UGYB?&hxuQ^(f5{2CpaM$5pkNYI?^Kj4N-iF(OD=Me96W@sI zCtn-xYQq0n{Dl8ZT>K(kltKJ{TpRA|xcdnIL|oE+ft!mz2~5(!e;sZ%{s8U~+_kuF z!tcec$9)8M2jNfPX5)VecO(Ae;-`Gk#@<7Caw2JP$KkdRpMzUW{1WjKz6N(T;ahQg zaDRe(lRr)%bsbn@qhg(%p!= z4z~ce9(M_@LA@2Y+wk9yJB9R*;+~|ur*O{_--Vk@{Oh<+65flu5%+JnvvE@qJ2{Ix z1-G4JDNx%~R#RQ!E^AmD-cVT^vK54D%R|+5weH%wkh^+AeNCkVS60{-)mJu@4aDHt zP#u!+qB?hDxO|;kK#aSt!Cg_^P+1QbB zm9^D$OA70+JP(+&o(R>bhRS=wbg{zb8)zggtP3~L;5FgKO|}AQLghvp+i08LP}kTv z)yi2@UAz8kUBnx5mkk-SK;uPTRaU5MjSOv5WkXd>U9+u}5#Ws59q9Cwm4iDDx>Y?m6a9rFi=xd zxwfpv-B?+<-d0jsRx!1%wq}!AtT};$jgihZ+VUFe*3&3Cmu!V)p>+f6%FS^i<6Tj= zfzg+*Di)fVhT#j>gw%p-sI14wf|D*&r8TY<)UD>Axh~9n)|GRvnd4krwxM!xO|TYO ze02>KERpi{3}q8&fGWz@zbaKVl^g0g{ssmT9AHgdO?5ddj83>KYs>4xvR2L2y@B;t zww4K?^_7h>^==txTY!}ks;;W8Y@pH&OizWaw0Z-+%8m7EffP5?h3d-dYOLkCtgI$% zwN)lT)-DBBRo7BrMH%C6mTHapnoXff2L7Hft*IPrYPhkoq0zR4-c>T516xw<*V?I# zDx8(s7^UMXBWs&qR;zjmBv@OkhK!M9dinuDQDvyPu3^2qx{-z4Tx%QR z57$ys`8u`O`hzPP>gwysUS3yQ3rrN~vZk`UjGAOI$wCh?V4#j=4V8dG!Y@i&wuY|T z<_~68IaEI?P4KoRTvf$_YgBuAaBfQf%eYC4`}=COMwaV0rjrpEOk)iRdy-+OVpDv> zthgK3g+ppU2DVf~B@L6M+Xzl>j%s5)TZ}Ot>`#d8!fpo991=)Q83>!d4ro6x&}?kd z$@X!tBVx#D)ZZi4JWcaH6!2r0IGNtzOs z=C{s26Y^)r^Mt(ODCK*<<0Zbwso3w?#%BrA7Lr(78?h5r_yI?VaKka4kRJ7Wug$^t zN5kazxcxrTUp>mlx7{YcyBrd7Ht{~b|2k|YzmrrsAKN4(*Cw@lRme@Mg|9juCHz;a zZOJxiVMdbtZg)J(?@v_9boG0KZ6+-lt$O!MmHvZa^GGvZ#r|&<@*DNrYm+t~a{QUP z4yy8Rv`J47J0$%A+bX^Prjg0wv^;>hzmwe-^Y^bfQu_%tz`9hx6bG(GgYVp)>YN%ej zF66!;`-W`y!mt49O1HPBaw99Tq0C)U2j&uLbeDz!8Jkj5hae^_eKyAR?9KX3*Sj~= zRa95ul5M)ex}b%t*QhPBj^)iZ<@<6HIU6Q}RTSEFpWTCLZf{W${m-+%l3nZ62P*@xc z83iIX#f`QW8RmBB?(xUt&OQyL4Med?S?)+u>MWyZ~ekp`DrCM#u z%XbF@-n?MG8=9Pax4V}V=g$v#gV)pClKlCl*OR~hixg1LOY#>lr7Y5<`n-kS1$4m5 z+V5TN=ay>`HI~q;B};>)()xmuMTPF*q9vp*a4%hwPX%0zlQorbqp?e_cQ5zn)3g$4 zv6uhnmj)IUNop!BEx~)eyC{D_Ft8xMXnwxbxkyr{mM$tG;ZjCbWw_q$EeR}<8X@x~ zHwh__GK%Pql&FSV=7m!L_mNiN>0`aLp(JoerCLc;#P_axAIh7UpAj|b~wENXj(m=lXHEcA~D?ado zP~`yrDOFiv(llzVudZGDwNUYz`HH}AD~ds_lq*jASkEy${uBLK+sG%mmi6oMX`LKcu>nbYyL&6PU3ZaWfNK%^N zgRTBAVSx&(OfzNaIzi@Cpw#ru&HGx+va*J1IjYT;hh&)M)3m`#CB5|B8?xZ5>STXt zpw^^7ng6^^15^GFV)Hjzy_Padg^aLPX>D0eF$9y$cYjG%n}TJHp?pOin`s%ka80G; zm&B4t>a$ub6o^a^AxlCP7qw`KBDn{KS$K`4Duou3iu=o1&g9oMH(r!X_Qta6%H~B? zQmg7hA%}tKk&Huj3cg~<#esE;zob&)B{d#JNA`a&8CVbVizRM|pJg++qXxtK*Ueza zqHw4_92&?!WY&kyE$OAwug#(rS}deRWrI04gk3b^OKK`B>s3^#FbN>5hOUmKwHt%xS8&1%k3{J*Ye+KxiyyCV!8ifxj(nu9hQ5*a*tZ>Da#f7QvT8`_X^9MX1RHm zyUKDmTJA%Z`!maZ+HzmA++&verR9#boAq38xp|hm+HzYg_q&$+OUvD9xjmM9%yLgz zZnAnn%RE_bw&mtoZn5RwX}PtQd!OZQv)mtA?vs|g$8wKY?kUSn9%lAqyyaeRxi?wv zLd#ucxwV$N&2oQYx&8C`qvp)(;QT^Fl9ImU9Mr6;J ze%0iZ+g(djXXV^9{F+Jmqefny>n&N9=@{#roqkKk#3{GlF!RQ7H(!#FT$r}RcJ25g z%`+zNuB)eBnmDy|wSAg<)pc1{F6b{bZogvA^%I5_%$qm>&(Xu!I(OOG^ut+sC|^ zkmpF|yQL-BW_zH;v9-l9W|Zsyv~M;)O$1>ao|&$=K!GJ_pai5Kv_upC01u||Vxr0K zRO}}$ARs7Tq9+rRMZI~^qzC^7P9`X51O)sRjG7SSA|8A2ZsN^%x~%PDF&;H}x0&g7 zKa!`<{3e;~+XgK|MLbk0P<^mw;6i`(bmdv=M%Bg1k)w(B>qIqm<#_ZeQC&LR-PD%s z92_2M93`qQ|FKBAB~*9$Q23UNX9E@4b2p{z3!dpsb)4=wGL~ueT{|@~-b{aVRI)%r zLM_~k5a#k|gX{_JcZbY45_Cue=y z8I{g=Lut)XTeKr<^mdz?4ktC5TAJ4^nN1~yuwqG!_eu+d zY=dnGPsx}Q!HM`M5>faa4X^W^buzE>DU-DAUFSP?oO9f>0XDz}*Z><~18jf|umLu} K2H3#=Yv3op{z!!Y literal 0 HcmV?d00001 diff --git a/build/bootstrap/zipobj.com b/build/bootstrap/zipobj.com index 1980a90183323c14059861219197de3a4960a79e..c4702c1f843d29ca392772b55c9e316c6c1b4620 100755 GIT binary patch delta 67350 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 diff --git a/build/rules.mk b/build/rules.mk index 34c2359c8..e999984c7 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -29,7 +29,6 @@ o/%.o: %.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx o/%.o: o/%.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $< o/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $< o/%.inc: %.h ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) -D__ASSEMBLER__ -P $< -o/%.pkg: ; @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^) o/%.h.ok: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $< o/%.okk: % ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $< o/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $< @@ -75,7 +74,6 @@ o/$(MODE)/%.ncabi.o: %.ncabi.c ; @$(COMPILE) -AOBJECTIFY.nc $(OBJECTIFY.ncab o/$(MODE)/%.real.o: %.c ; @$(COMPILE) -AOBJECTIFY.real $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.runs: o/$(MODE)/% ; @$(COMPILE) -ACHECK -tT$@ $< $(TESTARGS) -o/$(MODE)/%.pkg: ; @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^) o/$(MODE)/%.zip.o: % ; @$(COMPILE) -AZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $< o/$(MODE)/%-gcc.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $< o/$(MODE)/%-gcc.asm: %.cc ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $< @@ -87,6 +85,14 @@ o/%.a: $(file >$@.args,$^) @$(COMPILE) -AARCHIVE -T$@ $(AR) $(ARFLAGS) $@ @$@.args +o/%.pkg: + $(file >$@.args,$(filter %.o,$^)) + @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$@.args + +o/$(MODE)/%.pkg: + $(file >$@.args,$(filter %.o,$^)) + @$(COMPILE) -APACKAGE -T$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$@.args + o/$(MODE)/%.o: %.py o/$(MODE)/third_party/python/pyobj.com @$(COMPILE) -APYOBJ o/$(MODE)/third_party/python/pyobj.com $(PYFLAGS) -o $@ $< diff --git a/examples/shell.c b/examples/cosh.c similarity index 98% rename from examples/shell.c rename to examples/cosh.c index 9c2ab8915..822fdb4d3 100644 --- a/examples/shell.c +++ b/examples/cosh.c @@ -32,11 +32,11 @@ #include "third_party/linenoise/linenoise.h" /** - * @fileoverview Shell that works on Windows. + * @fileoverview Cosmopolitan Shell * * This doesn't have script language features like UNBOURNE.COM but it - * works on Windows and, unlike CMD.EXE, actually has CTRL-P and CTRL-R - * which alone make it so much better. + * works on Windows and, unlike CMD.EXE, has CTRL-P, CTRL-R, and other + * GNU Emacs / Readline keyboard shortcuts. * * One day we'll have UNBOURNE.COM working on Windows but the code isn't * very maintainable sadly. @@ -184,8 +184,8 @@ int main(int argc, char *argv[]) { args = xrealloc(args, (++n + 1) * sizeof(*args)); args[n - 1] = arg; args[n - 0] = 0; - start = 0; } + start = 0; } if (n > 0) { if ((prog = commandv(args[0], path, sizeof(path)))) { diff --git a/examples/cp.c b/examples/cp.c deleted file mode 100644 index ddb4910c3..000000000 --- a/examples/cp.c +++ /dev/null @@ -1,96 +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/calls/copyfile.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" -#include "libc/runtime/gc.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/ex.h" -#include "libc/sysv/consts/exit.h" -#include "libc/sysv/consts/ok.h" -#include "libc/x/x.h" -#include "third_party/getopt/getopt.h" - -#define USAGE \ - " SRC... DST\n\ -\n\ -SYNOPSIS\n\ -\n\ - Copies Files\n\ -\n\ -FLAGS\n\ -\n\ - -?\n\ - -h help\n\ - -f force\n\ - -n no clobber\n\ - -a preserve all\n\ - -p preserve owner and timestamps\n\ -\n" - -int flags; -bool force; - -wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "%s%s%s", "Usage: ", program_invocation_name, USAGE); - exit(rc); -} - -void GetOpts(int argc, char *argv[]) { - int opt; - while ((opt = getopt(argc, argv, "?hfnap")) != -1) { - switch (opt) { - case 'f': - force = true; - break; - case 'n': - flags |= COPYFILE_NOCLOBBER; - break; - case 'a': - case 'p': - flags |= COPYFILE_PRESERVE_OWNER; - flags |= COPYFILE_PRESERVE_TIMESTAMPS; - break; - case 'h': - case '?': - PrintUsage(EXIT_SUCCESS, stdout); - default: - PrintUsage(EX_USAGE, stderr); - } - } -} - -int cp(const char *src, const char *dst) { - if (endswith(dst, "/") || isdirectory(dst)) { - dst = _gc(xasprintf("%s/%s", dst, basename(src))); - } - if (!force && access(dst, W_OK) == -1 && errno != ENOENT) goto OnFail; - if (copyfile(src, dst, flags) == -1) goto OnFail; - return 0; -OnFail: - fprintf(stderr, "%s %s %s: %s\n", "error: cp", src, dst, strerror(errno)); - return -1; -} - -int main(int argc, char *argv[]) { - int i; - GetOpts(argc, argv); - if (argc - optind < 2) PrintUsage(EX_USAGE, stderr); - for (i = optind; i < argc - 1; ++i) { - if (cp(argv[i], argv[argc - 1]) == -1) { - return -1; - } - } - return 0; -} diff --git a/examples/echo.c b/examples/echo.c deleted file mode 100644 index b1f757779..000000000 --- a/examples/echo.c +++ /dev/null @@ -1,29 +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/stdio/stdio.h" -#include "libc/str/str.h" - -int main(int argc, char *argv[]) { - int i, j; - bool wantnewline; - if (argc > 1 && !strcmp(argv[1], "-n")) { - i = 2; - wantnewline = false; - } else { - i = 1; - wantnewline = true; - } - for (j = 0; i + j < argc; ++j) { - if (j) fputc(' ', stdout); - fputs(argv[i + j], stdout); - } - if (wantnewline) fputc('\n', stdout); - return 0; -} diff --git a/examples/examples.mk b/examples/examples.mk index 75002a90f..5501d524e 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -3,6 +3,12 @@ PKGS += EXAMPLES +ifeq ($(MODE),tiny) +EXAMPLES_BOOTLOADER = $(CRT) $(APE) +else +EXAMPLES_BOOTLOADER = $(CRT) $(APE_NO_MODIFY_SELF) +endif + EXAMPLES_FILES := $(wildcard examples/*) EXAMPLES_MAINS_S = $(filter %.S,$(EXAMPLES_FILES)) EXAMPLES_MAINS_C = $(filter %.c,$(EXAMPLES_FILES)) @@ -100,8 +106,7 @@ o/$(MODE)/examples/%.com.dbg: \ $(EXAMPLES_DEPS) \ o/$(MODE)/examples/%.o \ o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE) + $(EXAMPLES_BOOTLOADER) @$(APELINK) o/$(MODE)/examples/nomodifyself.com.dbg: \ @@ -112,21 +117,12 @@ o/$(MODE)/examples/nomodifyself.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/examples/greenbean.com.dbg: \ - $(EXAMPLES_DEPS) \ - o/$(MODE)/examples/greenbean.o \ - o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - o/$(MODE)/examples/hellolua.com.dbg: \ $(EXAMPLES_DEPS) \ o/$(MODE)/examples/hellolua.o \ o/$(MODE)/examples/hellolua.lua.zip.o \ o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE) + $(EXAMPLES_BOOTLOADER) @$(APELINK) o/$(MODE)/examples/ispell.com.dbg: \ @@ -134,8 +130,7 @@ o/$(MODE)/examples/ispell.com.dbg: \ o/$(MODE)/examples/ispell.o \ o/$(MODE)/usr/share/dict/words.zip.o \ o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE) + $(EXAMPLES_BOOTLOADER) @$(APELINK) o/$(MODE)/examples/nesemu1.com.dbg: \ @@ -145,8 +140,7 @@ o/$(MODE)/examples/nesemu1.com.dbg: \ o/$(MODE)/usr/share/rom/zelda.nes.zip.o \ o/$(MODE)/usr/share/rom/tetris.nes.zip.o \ o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE) + $(EXAMPLES_BOOTLOADER) @$(APELINK) o/$(MODE)/examples/nesemu1.com: \ @@ -160,12 +154,15 @@ o/$(MODE)/examples/nesemu1.com: \ o/$(MODE)/examples/.nesemu1/.symtab o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m +o/$(MODE)/usr/share/dict/words.zip.o: ZIPOBJ_FLAGS += -C2 $(EXAMPLES_OBJS): examples/examples.mk -usr/share/dict/words: usr/share/dict/words.gz - @mkdir -p $(@D) - @$(GZ) $(ZFLAGS) -d <$< >$@ +o/$(MODE)/usr/share/dict/words: \ + usr/share/dict/words.gz \ + o/$(MODE)/tool/build/gzip.com + @$(MKDIR) $(@D) + @o/$(MODE)/tool/build/gzip.com $(ZFLAGS) -cd <$< >$@ .PHONY: o/$(MODE)/examples o/$(MODE)/examples: \ diff --git a/examples/package/build.mk b/examples/package/build.mk index fc604b2a2..acf3bf46b 100644 --- a/examples/package/build.mk +++ b/examples/package/build.mk @@ -60,7 +60,7 @@ o/$(MODE)/examples/package/%.com.dbg: \ $(EXAMPLES_PACKAGE_DEPS) \ o/$(MODE)/examples/package/%.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) # Invalidates objects in package when makefile is edited. diff --git a/examples/pyapp/pyapp.mk b/examples/pyapp/pyapp.mk index 298236fc6..49e88b386 100644 --- a/examples/pyapp/pyapp.mk +++ b/examples/pyapp/pyapp.mk @@ -95,7 +95,7 @@ o/$(MODE)/examples/pyapp/pyapp.com.dbg: \ o/$(MODE)/examples/pyapp/pyapp.pkg \ o/$(MODE)/examples/pyapp/pyapp.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) $(LINK) $(LINKARGS) -o $@ # # Unwrap the APE .COM binary, that's embedded within the linked file diff --git a/examples/touch.c b/examples/touch.c deleted file mode 100644 index 58e179f44..000000000 --- a/examples/touch.c +++ /dev/null @@ -1,29 +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/errno.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" - -/** - * @fileoverview Command for updating timestamps on files. - */ - -int main(int argc, char *argv[]) { - int i; - for (i = 1; i < argc; ++i) { - if (touch(argv[i], 0666) == -1) { - fprintf(stderr, "ERROR: %s: %s\n", argv[i], strerror(errno)); - exit(1); - } - } - return 0; -} diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 6e359fdbe..cceeec7e3 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -112,6 +112,7 @@ o/$(MODE)/libc/calls/execl.o \ o/$(MODE)/libc/calls/execle.o \ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/execve-sysv.o \ +o/$(MODE)/libc/calls/execve-nt.greg.o \ o/$(MODE)/libc/calls/mkntenvblock.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 8b4a962ed..254273c63 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -76,7 +76,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { /** * Returns pointer to fastest clock_gettime(). */ -clock_gettime_f *__get_clock_gettime(bool *opt_out_isfast) { +clock_gettime_f *__clock_gettime_get(bool *opt_out_isfast) { bool isfast; clock_gettime_f *res; if (IsLinux() && (res = __vdsosym("LINUX_2.6", "__vdso_clock_gettime"))) { @@ -99,6 +99,6 @@ clock_gettime_f *__get_clock_gettime(bool *opt_out_isfast) { hidden int __clock_gettime_init(int clockid, struct timespec *ts) { clock_gettime_f *gettime; - __clock_gettime = gettime = __get_clock_gettime(0); + __clock_gettime = gettime = __clock_gettime_get(0); return gettime(clockid, ts); } diff --git a/libc/calls/clock_gettime.h b/libc/calls/clock_gettime.h index 0fa6758a6..e6afedd45 100644 --- a/libc/calls/clock_gettime.h +++ b/libc/calls/clock_gettime.h @@ -8,7 +8,7 @@ typedef int clock_gettime_f(int, struct timespec *); extern clock_gettime_f *__clock_gettime; hidden clock_gettime_f __clock_gettime_init; -hidden clock_gettime_f *__get_clock_gettime(bool *); +hidden clock_gettime_f *__clock_gettime_get(bool *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/execve-nt.greg.c b/libc/calls/execve-nt.greg.c index 3418744c3..4bb7c8794 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/calls/execve-nt.greg.c @@ -23,6 +23,7 @@ #include "libc/calls/ntspawn.h" #include "libc/calls/strace.internal.h" #include "libc/calls/syscall-nt.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/mem/alloca.h" #include "libc/nt/accounting.h" #include "libc/nt/console.h" @@ -59,13 +60,28 @@ textwindows int sys_execve_nt(const char *program, char *const argv[], int rc; size_t i; uint32_t dwExitCode; + char progbuf[PATH_MAX]; struct MemoryIntervals *mm; struct NtStartupInfo startinfo; struct NtProcessInformation procinfo; + if (strlen(program) + 4 + 1 > PATH_MAX) { + return enametoolong(); + } + // this is a non-recoverable operation, so do some manual validation if (sys_faccessat_nt(AT_FDCWD, program, X_OK, 0) == -1) { - return eacces(); + stpcpy(stpcpy(progbuf, program), ".com"); + if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) { + program = progbuf; + } else { + stpcpy(stpcpy(progbuf, program), ".exe"); + if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) { + program = progbuf; + } else { + return eacces(); + } + } } ////////////////////////////////////////////////////////////////////////////// diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 7c39f32c0..bcf848537 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -16,28 +16,80 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/errno.h" #include "libc/mem/alloca.h" #include "libc/paths.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/ok.h" +#include "libc/sysv/errfuns.h" + +static bool CanExecute(const char *path) { + return !sys_faccessat(AT_FDCWD, path, X_OK, 0); +} + +static bool IsApeBinary(const char *path) { + int fd; + char buf[8]; + bool res = false; + if ((fd = sys_open(path, O_RDONLY, 0)) != -1) { + if (sys_read(fd, buf, 8) == 8 && READ64LE(buf) == READ64LE("MZqFpD='")) { + res = true; + } + sys_close(fd); + } + return res; +} + +static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) { + size_t n, m; + n = strlen(a); + m = strlen(b); + if (n + 1 + m + 1 < PATH_MAX) { + stpcpy(stpcpy(stpcpy(buf, a), "/"), b); + return buf; + } else { + return ""; + } +} int sys_execve(const char *prog, char *const argv[], char *const envp[]) { + int e; size_t i; + char *buf; char **shargs; + const char *ape; + e = errno; __sys_execve(prog, argv, envp); if (errno != ENOEXEC) return -1; for (i = 0; argv[i];) ++i; - shargs = alloca((i + 2) * sizeof(char *)); - memcpy(shargs + 2, argv + 1, i * sizeof(char *)); - if (IsFreebsd() || IsNetbsd()) { - shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX), PATH_MAX), - _PATH_BSHELL); + buf = alloca(PATH_MAX); + shargs = alloca((i + 4) * sizeof(char *)); + if (IsApeBinary(prog) && + (CanExecute((ape = "/usr/bin/ape")) || + CanExecute( + (ape = Join(firstnonnull(getenv("TMPDIR"), "/tmp"), "ape", buf))))) { + shargs[0] = ape; + shargs[1] = "-"; + shargs[2] = prog; + memcpy(shargs + 3, argv, (i + 1) * sizeof(char *)); + } else if (CanExecute(prog)) { + if (IsFreebsd() || IsNetbsd()) { + shargs[0] = firstnonnull(commandv("bash", buf, PATH_MAX), _PATH_BSHELL); + } else { + shargs[0] = _PATH_BSHELL; + } + shargs[1] = prog; + memcpy(shargs + 2, argv + 1, i * sizeof(char *)); } else { - shargs[0] = _PATH_BSHELL; + return enoexec(); } - shargs[1] = prog; + errno = e; return __sys_execve(shargs[0], shargs, envp); } diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index ed6c962d6..3982ef5cb 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -32,14 +32,21 @@ textwindows char *sys_getcwd_nt(char *buf, size_t size) { if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) { if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) { tprecode16to8(buf, size, p); + i = 0; j = 0; if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { + // turn c:\... into \c\... + p[1] = p[0]; + p[0] = '\\'; + } else if (n >= 7 && p[0] == '\\' && p[1] == '\\' && p[2] == '?' && + p[3] == '\\' && isalpha(p[4]) && p[5] == ':' && p[6] == '\\') { + // turn \\?\c:\... into \c\... buf[j++] = '/'; + buf[j++] = p[4]; buf[j++] = '/'; - buf[j++] = '?'; - buf[j++] = '/'; + i += 7; } - for (i = 0; i < n;) { + while (i < n) { x = p[i++] & 0xffff; if (!IsUcs2(x)) { if (i < n) { diff --git a/libc/calls/getntsyspath.S b/libc/calls/getntsyspath.S index f4969bcf4..d3e4466fb 100644 --- a/libc/calls/getntsyspath.S +++ b/libc/calls/getntsyspath.S @@ -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/macros.internal.h" // Obtains WIN32 magic path, e.g. GetTempPathA. @@ -33,7 +34,13 @@ __getntsyspath: movpp %rdi,%rcx # call f=%rax(p1=%rcx,p2=%rdx) sub $40,%rsp call *%rax - xor %edx,%edx + testb IsWindows() + jz 3f + mov (%rdi),%cl # turn c:\... into \c\... + movb $'\\',(%rdi) + mov %cl,1(%rdi) + movb $'\\',2(%rdi) +3: xor %edx,%edx mov -8(%rbp),%ecx # restore %edx param as %ecx cmp %eax,%ecx # use current dir on overflow cmovbe %edx,%eax diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index 3c34e73c1..7a516ebb8 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -34,6 +34,10 @@ char program_executable_name[PATH_MAX]; +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; @@ -56,16 +60,16 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { if (IsWindows()) { n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16)); for (i = 0; i < n; ++i) { + // turn c:\foo\bar into c:/foo/bar if (u.path16[i] == '\\') { u.path16[i] = '/'; } } - if (isalpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { - p[0] = '/'; - p[1] = '/'; - p[2] = '?'; - p[3] = '/'; - p += 4; + if (IsAlpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { + // turn c:/... into /c/... + u.path16[1] = u.path16[0]; + u.path16[0] = '/'; + u.path16[2] = '/'; } tprecode16to8(p, e - p, u.path16); return; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index f8315ad52..e076efbe5 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -17,8 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.internal.h" +#include "libc/bits/bits.h" #include "libc/calls/ntspawn.h" #include "libc/fmt/conv.h" +#include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/mem/alloca.h" #include "libc/mem/mem.h" @@ -31,15 +33,85 @@ #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) -static int CompareStrings(const char *l, const char *r) { +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +static inline char *StrChr(char *s, int c) { + for (;; ++s) { + if ((*s & 255) == (c & 255)) return s; + if (!*s) return 0; + } +} + +static textwindows inline int CompareStrings(const char *l, const char *r) { int a, b; size_t i = 0; while ((a = ToUpper(l[i] & 255)) == (b = ToUpper(r[i] & 255)) && r[i]) ++i; return a - b; } -static void InsertString(char **a, size_t i, char *s) { - size_t j; +static textwindows void FixPath(char *path) { + char *p; + size_t i; + + // skip over variable name + while (*path++) { + if (path[-1] == '=') { + break; + } + } + + // turn colon into semicolon + // unless it already looks like a dos path + for (p = path; *p; ++p) { + if (p[0] == ':' && p[1] != '\\') { + p[0] = ';'; + } + } + + // turn \c\... into c:\... + p = path; + if (p[0] == '/' && IsAlpha(p[1]) && p[2] == '/') { + p[0] = p[1]; + p[1] = ':'; + } + for (; *p; ++p) { + if (p[0] == ';' && p[1] == '/' && IsAlpha(p[2]) && p[3] == '/') { + p[1] = p[2]; + p[2] = ':'; + } + } + + // turn slash into backslash + for (p = path; *p; ++p) { + if (*p == '/') { + *p = '\\'; + } + } +} + +static textwindows void InsertString(char **a, size_t i, char *s, + char buf[ARG_MAX], size_t *bufi) { + char *v; + size_t j, k; + + // apply fixups to var=/c/... + if ((v = StrChr(s, '=')) && v[1] == '/' && IsAlpha(v[2]) && v[3] == '/') { + v = buf + *bufi; + for (k = 0; s[k]; ++k) { + if (*bufi + 1 < ARG_MAX) { + buf[(*bufi)++] = s[k]; + } + } + if (*bufi < ARG_MAX) { + buf[(*bufi)++] = 0; + FixPath(v); + s = v; + } + } + + // append to sorted list for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) { a[j] = a[j - 1]; } @@ -58,18 +130,18 @@ static void InsertString(char **a, size_t i, char *s) { * @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767) */ textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[], - const char *extravar) { + const char *extravar, char buf[ARG_MAX]) { bool v; char *t; axdx_t rc; uint64_t w; char **vars; wint_t x, y; - size_t i, j, k, n, m; + size_t i, j, k, n, m, bufi = 0; for (n = 0; envp[n];) n++; vars = alloca((n + 1) * sizeof(char *)); - for (i = 0; i < n; ++i) InsertString(vars, i, envp[i]); - if (extravar) InsertString(vars, n++, extravar); + for (i = 0; i < n; ++i) InsertString(vars, i, envp[i], buf, &bufi); + if (extravar) InsertString(vars, n++, extravar, buf, &bufi); for (k = i = 0; i < n; ++i) { j = 0; v = false; diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index e69732c60..9342bffd6 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -31,6 +31,10 @@ static inline bool IsSlash(char c) { return c == '/' || c == '\\'; } +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + textwindows static const char *FixNtMagicPath(const char *path, unsigned flags) { const struct NtMagicPaths *mp = &kNtMagicPaths; @@ -78,18 +82,51 @@ textwindows int __mkntpath2(const char *path, */ char16_t *p; const char *q; - size_t i, n, m, z; + bool isdospath; + size_t i, n, m, x, z; if (!path) return efault(); path = FixNtMagicPath(path, flags); p = path16; q = path; - if (IsSlash(path[0]) && IsSlash(path[1]) && path[2] == '?' && - IsSlash(path[3])) { + + if (IsSlash(q[0]) && IsAlpha(q[1]) && IsSlash(q[2])) { z = MIN(32767, PATH_MAX); + // turn "\c\foo" into "\\?\c:\foo" + p[0] = '\\'; + p[1] = '\\'; + p[2] = '?'; + p[3] = '\\'; + p[4] = q[1]; + p[5] = ':'; + p[6] = '\\'; + p += 7; + q += 3; + z -= 7; + x = 7; + } else if (IsSlash(q[0]) && IsAlpha(q[1]) && IsSlash(q[2])) { + z = MIN(32767, PATH_MAX); + // turn "c:\foo" into "\\?\c:\foo" + p[0] = '\\'; + p[1] = '\\'; + p[2] = '?'; + p[3] = '\\'; + p[4] = q[0]; + p[5] = ':'; + p[6] = '\\'; + p += 7; + q += 3; + z -= 7; + x = 7; + } else if (IsSlash(q[0]) && IsSlash(q[1]) && q[2] == '?' && IsSlash(q[3])) { + z = MIN(32767, PATH_MAX); + x = 0; } else { z = MIN(260, PATH_MAX); + x = 0; } - if (IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' && + + // turn /tmp into GetTempPath() + if (!x && IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' && (IsSlash(q[4]) || !q[4])) { m = GetTempPath(z, p); if (!q[4]) return m; @@ -99,15 +136,20 @@ textwindows int __mkntpath2(const char *path, } else { m = 0; } + + // turn utf-8 into utf-16 n = tprecode8to16(p, z, q).ax; if (n >= z - 1) { STRACE("path too long for windows: %#s", path); return enametoolong(); } + + // turn slash into backslash for (i = 0; i < n; ++i) { if (p[i] == '/') { p[i] = '\\'; } } - return m + n; + + return x + m + n; } diff --git a/libc/calls/now.c b/libc/calls/now.c index 83012eba4..62ec67940 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -102,7 +102,7 @@ static long double nowl_vdso(void) { long double nowl_setup(void) { bool isfast; uint64_t ticks; - __gettime = __get_clock_gettime(&isfast); + __gettime = __clock_gettime_get(&isfast); if (isfast) { nowl = nowl_vdso; } else if (X86_HAVE(INVTSC)) { diff --git a/libc/calls/ntaccesscheck.c b/libc/calls/ntaccesscheck.c index b0769a6a6..367811257 100644 --- a/libc/calls/ntaccesscheck.c +++ b/libc/calls/ntaccesscheck.c @@ -94,15 +94,15 @@ TryAgain: } } else { rc = __winerr(); - STRACE("%s failed: %m", "AccessCheck"); + STRACE("%s(%#hs) failed: %m", "AccessCheck", pathname); } } else { rc = __winerr(); - STRACE("%s failed: %m", "DuplicateToken"); + STRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname); } } else { rc = __winerr(); - STRACE("%s failed: %m", "OpenProcessToken"); + STRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname); } } else { e = GetLastError(); @@ -112,9 +112,11 @@ TryAgain: goto TryAgain; } else { rc = enomem(); + STRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); } } else { errno = e; + STRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); rc = -1; } } diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 19a7c560e..9f99129b6 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -36,8 +36,9 @@ struct SpawnBlock { struct { char16_t cmdline[ARG_MAX / 2]; char16_t envvars[ARG_MAX / 2]; + char buf[ARG_MAX]; }; - char __pad[ROUNDUP(ARG_MAX / 2 * 2 * sizeof(char16_t), FRAMESIZE)]; + char __pad[ROUNDUP(ARG_MAX / 2 * 3 * sizeof(char16_t), FRAMESIZE)]; }; }; @@ -83,7 +84,7 @@ textwindows int ntspawn( (block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, sizeof(*block), 0)) && mkntcmdline(block->cmdline, prog, argv) != -1 && - mkntenvblock(block->envvars, envp, extravar) != -1 && + mkntenvblock(block->envvars, envp, extravar, block->buf) != -1 && CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, opt_lpThreadAttributes, bInheritHandles, dwCreationFlags | kNtCreateUnicodeEnvironment, diff --git a/libc/calls/ntspawn.h b/libc/calls/ntspawn.h index df1955b43..167b431f1 100644 --- a/libc/calls/ntspawn.h +++ b/libc/calls/ntspawn.h @@ -7,7 +7,8 @@ COSMOPOLITAN_C_START_ int mkntcmdline(char16_t[ARG_MAX / 2], const char *, char *const[]) hidden; -int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *) hidden; +int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *, + char[ARG_MAX]) hidden; int ntspawn(const char *, char *const[], char *const[], const char *, struct NtSecurityAttributes *, struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *, const struct NtStartupInfo *, diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index e8f730dc2..3e75adefc 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -58,10 +58,9 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset); if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { - buf[j++] = '/'; - buf[j++] = '/'; - buf[j++] = '?'; - buf[j++] = '/'; + p[1] = p[0]; + p[0] = '/'; + p[2] = '/'; } while (i < n) { x = p[i++] & 0xffff; diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index b8d3cc662..70d158183 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -9,7 +9,7 @@ #define _KERNTRACE 0 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 1 /* not configurable w/ flag yet */ +#define _NTTRACE 0 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h index 479b6c6ed..b73fc3b50 100644 --- a/libc/calls/syscall-sysv.internal.h +++ b/libc/calls/syscall-sysv.internal.h @@ -64,6 +64,7 @@ i32 sys_mknod(const char *, u32, u64) hidden; i32 sys_mprotect(void *, u64, i32) hidden; i32 sys_msync(void *, u64, i32) hidden; i32 sys_munmap(void *, u64) hidden; +i32 sys_open(const char *, i32, u32) hidden; i32 sys_openat(i32, const char *, i32, u32) hidden; i32 sys_pause(void) hidden; i32 sys_pipe(i32[hasatleast 2]) hidden; diff --git a/libc/fmt/stoa.c b/libc/fmt/stoa.c index f7efa15ac..94973bb9a 100644 --- a/libc/fmt/stoa.c +++ b/libc/fmt/stoa.c @@ -62,19 +62,6 @@ static int __fmt_stoa_quoted(out_f out, void *a, uint64_t w) { return out(buf, a, w ? (bsr(w) >> 3) + 1 : 1); } -static int __fmt_stoa_quote(out_f out, void *arg, unsigned flags, char ch, - unsigned char signbit) { - if (flags & FLAGS_REPR) { - if (signbit == 63) { - if (out("L", arg, 1) == -1) return -1; - } else if (signbit == 15) { - if (out("u", arg, 1) == -1) return -1; - } - if (out(&ch, arg, 1) == -1) return -1; - } - return 0; -} - /** * Converts string to array. * @@ -103,8 +90,6 @@ int __fmt_stoa(int out(const char *, void *, size_t), void *arg, void *data, if (flags & FLAGS_PRECISION) { precision = min(strlen(p), precision); } - } else { - if (__fmt_stoa_quote(out, arg, flags, qchar, signbit) == -1) return -1; } ignorenul = false; @@ -153,6 +138,9 @@ int __fmt_stoa(int out(const char *, void *, size_t), void *arg, void *data, } else if (weaken(strnwidth)) { w = weaken(strnwidth)(p, precision, 0); } + if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) { + w += 2 + (signbit == 63) + (signbit == 15); + } if (w < width) { pad = width - w; } @@ -162,6 +150,16 @@ int __fmt_stoa(int out(const char *, void *, size_t), void *arg, void *data, if (__fmt_pad(out, arg, pad) == -1) return -1; } + if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) { + if (signbit == 63) { + if (out("L", arg, 1) == -1) return -1; + } else if (signbit == 15) { + if (out("u", arg, 1) == -1) return -1; + } + buf[0] = qchar; + if (out(buf, arg, 1) == -1) return -1; + } + if (justdobytes) { while (precision--) { wc = *p++ & 0xff; @@ -207,14 +205,14 @@ int __fmt_stoa(int out(const char *, void *, size_t), void *arg, void *data, } } - if (pad && (flags & FLAGS_LEFT)) { - if (__fmt_pad(out, arg, pad) == -1) return -1; - } - if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) { buf[0] = qchar; if (out(buf, arg, 1) == -1) return -1; } + if (pad && (flags & FLAGS_LEFT)) { + if (__fmt_pad(out, arg, pad) == -1) return -1; + } + return 0; } diff --git a/libc/nexgen32e/gc.S b/libc/nexgen32e/gc.S index 08c03c60b..d93859808 100644 --- a/libc/nexgen32e/gc.S +++ b/libc/nexgen32e/gc.S @@ -22,6 +22,8 @@ #define INITIAL_CAPACITY 4 + nop + // Invokes deferred function calls. // // This offers behavior similar to std::unique_ptr. Functions @@ -32,8 +34,6 @@ // // @param rax,rdx,xmm0,xmm1,st0,st1 is return value // @see test/libc/runtime/gc_test.c - nop # backtrace workaround -// __gc: decq __garbage(%rip) mov __garbage(%rip),%r8 mov __garbage+16(%rip),%r9 @@ -43,17 +43,16 @@ __gc: decq __garbage(%rip) mov 8(%r8),%r9 mov 16(%r8),%rdi push 24(%r8) -// push %rbp mov %rsp,%rbp - sub $16,%rsp - push %rax - push %rdx - movdqa %xmm0,-16(%rbp) + sub $32,%rsp + mov %rax,-8(%rbp) + mov %rdx,-16(%rbp) + movdqa %xmm0,-32(%rbp) call *%r9 - movdqa -16(%rbp),%xmm0 - pop %rdx - pop %rax + movdqa -32(%rbp),%xmm0 + mov -16(%rbp),%rdx + mov -8(%rbp),%rax leave ret 9: hlt diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index aee215169..e902aaa54 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -35,23 +35,24 @@ const char *FindDebugBinary(void) { char *p; size_t n; if (!once) { - if (!(res = getenv("COMDBG"))) { - p = GetProgramExecutableName(); - n = strlen(p); - if (n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) { - res = p; - } else if (n > 4 && READ32LE(p + n - 4) == READ32LE(".com") && - n + 4 < ARRAYLEN(buf)) { - mempcpy(mempcpy(buf, p, n), ".dbg", 5); - if (fileexists(buf)) { - res = buf; - } - } else if (n + 8 < ARRAYLEN(buf)) { - mempcpy(mempcpy(buf, p, n), ".com.dbg", 9); - if (fileexists(buf)) { - res = buf; - } + p = GetProgramExecutableName(); + n = strlen(p); + if (n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) { + res = p; + } else if (n > 4 && READ32LE(p + n - 4) == READ32LE(".com") && + n + 4 < ARRAYLEN(buf)) { + mempcpy(mempcpy(buf, p, n), ".dbg", 5); + if (fileexists(buf)) { + res = buf; } + } else if (n + 8 < ARRAYLEN(buf)) { + mempcpy(mempcpy(buf, p, n), ".com.dbg", 9); + if (fileexists(buf)) { + res = buf; + } + } + if (!res) { + res = getenv("COMDBG"); } once = true; } diff --git a/libc/runtime/getdosenviron.c b/libc/runtime/getdosenviron.c index 58f3e3bf1..c9dd991b2 100644 --- a/libc/runtime/getdosenviron.c +++ b/libc/runtime/getdosenviron.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" @@ -23,6 +24,19 @@ #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) +forceinline 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) { + for (; n; --n, ++s) { + if ((*s & 255) == c) { + return s; + } + } + return 0; +} + static textwindows noasan noinstrument axdx_t Recode16to8(char *dst, size_t dstsize, const char16_t *src) { @@ -45,13 +59,51 @@ static textwindows noasan noinstrument axdx_t Recode16to8(char *dst, } w = tpenc(x); do { - if (r.ax + 1 >= dstsize) break; - dst[r.ax++] = w; + if (r.ax + 1 < dstsize) { + dst[r.ax++] = w; + } else { + break; + } } while ((w >>= 8)); } + if (r.ax < dstsize) { + dst[r.ax] = 0; + } return r; } +textwindows noinstrument noasan void FixPath(char *path) { + char *p; + size_t i; + + // turn backslash into slash + for (p = path; *p; ++p) { + if (*p == '\\') { + *p = '/'; + } + } + + // turn c:/... into /c/... + p = path; + if (IsAlpha(p[0]) && p[1] == ':' && p[2] == '/') { + p[1] = p[0]; + p[0] = '/'; + } + for (; *p; ++p) { + if (p[0] == ';' && IsAlpha(p[1]) && p[2] == ':' && p[3] == '/') { + p[2] = p[1]; + p[1] = '/'; + } + } + + // turn semicolon into colon + for (p = path; *p; ++p) { + if (*p == ';') { + *p = ':'; + } + } +} + /** * Transcodes NT environment variable block from UTF-16 to UTF-8. * @@ -66,12 +118,17 @@ textwindows noasan noinstrument int GetDosEnviron(const char16_t *env, char *buf, size_t size, char **envp, size_t max) { int i; + char *p; axdx_t r; i = 0; --size; while (*env) { if (i + 1 < max) envp[i++] = buf; r = Recode16to8(buf, size, env); + if ((p = memchr(buf, '=', r.ax)) && IsAlpha(p[1]) && p[2] == ':' && + (p[3] == '\\' || p[3] == '/')) { + FixPath(p + 1); + } size -= r.ax + 1; buf += r.ax + 1; env += r.dx; diff --git a/libc/x/x.h b/libc/x/x.h index a904caa93..c514b0e42 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -73,6 +73,7 @@ char *xdirname(const char *) paramsnonnull() _XMAL; char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL; char *xreadlink(const char *) paramsnonnull() _XMAL; char *xreadlinkat(int, const char *) paramsnonnull() _XMAL; +void xfixpath(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § eXtended apis » time ─╬─│┼ diff --git a/libc/x/xfixpath.c b/libc/x/xfixpath.c new file mode 100644 index 000000000..237eeb80f --- /dev/null +++ b/libc/x/xfixpath.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 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/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/x/x.h" + +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +/** + * Fixes $PATH environment variable on Windows. + */ +void xfixpath(void) { + size_t i; + char *p, *path; + path = strdup(getenv("PATH")); + if (strstr(path, "C:\\") && strstr(path, ";")) { + + // turn backslash into slash + for (p = path; *p; ++p) { + if (*p == '\\') *p = '/'; + } + + // turn c:/... into /c/... + if (IsAlpha(path[0]) && path[1] == ':' && path[2] == '/') { + path[1] = path[0]; + path[0] = '/'; + } + for (p = path, i = 0; p[i]; ++i) { + if (p[i + 0] == ';' && IsAlpha(p[i + 1]) && p[i + 2] == ':' && + p[i + 3] == '/') { + p[i + 2] = p[i + 1]; + p[i + 1] = '/'; + } + } + + // turn semicolon into colon + for (p = path; *p; ++p) { + if (*p == ';') *p = ':'; + } + + setenv("PATH", path, true); + } + free(path); +} diff --git a/test/dsp/core/test.mk b/test/dsp/core/test.mk index afa8171c6..60dbc21e8 100644 --- a/test/dsp/core/test.mk +++ b/test/dsp/core/test.mk @@ -43,7 +43,7 @@ o/$(MODE)/test/dsp/core/%.com.dbg: \ o/$(MODE)/test/dsp/core/%.o \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/dsp/core diff --git a/test/dsp/scale/test.mk b/test/dsp/scale/test.mk index 4b988b52a..87698b94b 100644 --- a/test/dsp/scale/test.mk +++ b/test/dsp/scale/test.mk @@ -50,7 +50,7 @@ o/$(MODE)/test/dsp/scale/%.com.dbg: \ o/$(MODE)/test/dsp/scale/scale.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/dsp/scale diff --git a/test/dsp/tty/test.mk b/test/dsp/tty/test.mk index c5b61ec0c..c8dd85733 100644 --- a/test/dsp/tty/test.mk +++ b/test/dsp/tty/test.mk @@ -45,7 +45,7 @@ o/$(MODE)/test/dsp/tty/%.com.dbg: \ o/$(MODE)/test/dsp/tty/tty.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/dsp/tty diff --git a/test/libc/alg/test.mk b/test/libc/alg/test.mk index 5ff888eff..8b3b5bfc2 100644 --- a/test/libc/alg/test.mk +++ b/test/libc/alg/test.mk @@ -50,7 +50,7 @@ o/$(MODE)/test/libc/alg/%.com.dbg: \ o/$(MODE)/test/libc/alg/alg.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_ALG_OBJS): test/libc/alg/test.mk diff --git a/test/libc/bits/test.mk b/test/libc/bits/test.mk index a588a980f..d4f59943e 100644 --- a/test/libc/bits/test.mk +++ b/test/libc/bits/test.mk @@ -46,7 +46,7 @@ o/$(MODE)/test/libc/bits/%.com.dbg: \ o/$(MODE)/test/libc/bits/bits.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_BITS_OBJS): \ diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c index ce54e88de..94d00d4bb 100644 --- a/test/libc/calls/clock_gettime_test.c +++ b/test/libc/calls/clock_gettime_test.c @@ -35,7 +35,7 @@ TEST(clock_gettime, test) { ASSERT_NE(0, ts.tv_sec); ASSERT_NE(0, ts.tv_nsec); if (__is_linux_2_6_23()) { - ASSERT_GT((intptr_t)__get_clock_gettime(&isfast), + ASSERT_GT((intptr_t)__clock_gettime_get(&isfast), getauxval(AT_SYSINFO_EHDR)); ASSERT_TRUE(isfast); } diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index af7eb7851..77f65f5a7 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -45,5 +45,5 @@ TEST(getcwd, testWindows_addsFunnyPrefix) { if (!IsWindows()) return; char path[PATH_MAX]; ASSERT_NE(0, getcwd(path, sizeof(path))); - EXPECT_STARTSWITH("//?/", path); + EXPECT_STARTSWITH("/C/", path); } diff --git a/test/libc/calls/mkntenvblock_test.c b/test/libc/calls/mkntenvblock_test.c index 3c7c4bac2..505b789fb 100644 --- a/test/libc/calls/mkntenvblock_test.c +++ b/test/libc/calls/mkntenvblock_test.c @@ -20,17 +20,18 @@ #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" +char tmp[ARG_MAX]; char16_t envvars[ARG_MAX / 2]; TEST(mkntenvblock, emptyList_onlyOutputsDoubleNulStringTerminator) { char *envp[] = {NULL}; - ASSERT_NE(-1, mkntenvblock(envvars, envp, NULL)); + ASSERT_NE(-1, mkntenvblock(envvars, envp, NULL, tmp)); ASSERT_BINEQ(u"  ", envvars); } TEST(mkntenvblock, envp_becomesSortedDoubleNulTerminatedUtf16String) { char *envp[] = {"u=b", "c=d", "韩=非", "uh=d", "hduc=d", NULL}; - ASSERT_NE(-1, mkntenvblock(envvars, envp, NULL)); + ASSERT_NE(-1, mkntenvblock(envvars, envp, NULL, tmp)); ASSERT_BINEQ(u"C = d   " u"H D U C = d   " u"U = b   " @@ -42,7 +43,7 @@ TEST(mkntenvblock, envp_becomesSortedDoubleNulTerminatedUtf16String) { TEST(mkntenvblock, extraVar_getsAdded) { char *envp[] = {"u=b", "c=d", "韩=非", "uh=d", "hduc=d", NULL}; - ASSERT_NE(-1, mkntenvblock(envvars, envp, "a=a")); + ASSERT_NE(-1, mkntenvblock(envvars, envp, "a=a", tmp)); ASSERT_BINEQ(u"A = a   " u"C = d   " u"H D U C = d   " @@ -52,3 +53,11 @@ TEST(mkntenvblock, extraVar_getsAdded) { u"  ", envvars); } + +TEST(mkntenvblock, pathvars_getUpdated) { + char *envp[] = {"PATH=/c/foo:/d/bar", NULL}; + ASSERT_NE(-1, mkntenvblock(envvars, envp, 0, tmp)); + ASSERT_BINEQ(u"P A T H = c : \\ f o o ; d : \\ b a r   " + u"  ", + envvars); +} diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index b178c07ee..86ecd4465 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -101,9 +101,10 @@ TEST(readlinkat, statReadsNameLength) { } TEST(readlinkat, realpathReturnsLongPath) { - if (!IsWindows()) return; struct stat st; char buf[PATH_MAX]; + if (!IsWindows()) return; + if (!startswith(getcwd(buf, PATH_MAX), "/c/")) return; ASSERT_SYS(0, 0, touch("froot", 0644)); - ASSERT_STARTSWITH("//?/", realpath("froot", buf)); + ASSERT_STARTSWITH("/c/", realpath("froot", buf)); } diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index bb21502ce..eaf0b18e1 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -61,7 +61,7 @@ o/$(MODE)/test/libc/calls/%.com.dbg: \ o/$(MODE)/test/libc/calls/calls.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/libc/calls diff --git a/test/libc/dns/test.mk b/test/libc/dns/test.mk index 4fe280243..c29aab197 100644 --- a/test/libc/dns/test.mk +++ b/test/libc/dns/test.mk @@ -53,7 +53,7 @@ o/$(MODE)/test/libc/dns/%.com.dbg: \ o/$(MODE)/test/libc/dns/dns.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/libc/dns diff --git a/test/libc/fmt/fmt_test.c b/test/libc/fmt/fmt_test.c index 9a3851dfb..3300ee9eb 100644 --- a/test/libc/fmt/fmt_test.c +++ b/test/libc/fmt/fmt_test.c @@ -278,6 +278,11 @@ TEST(fmt, p) { gc(xasprintf("% 10p", 0xffff800000031337))); } +TEST(fmt, quoted) { + ASSERT_STREQ(" \"hello\"", gc(xasprintf("%`*.*s", 10, 5, "hello"))); + ASSERT_STREQ("\"hello\" ", gc(xasprintf("%-`*.*s", 10, 5, "hello"))); +} + TEST(fmt, regress) { char buf[512]; const char *meth = "GET"; diff --git a/test/libc/fmt/test.mk b/test/libc/fmt/test.mk index 57c9c3262..ccc8603a5 100644 --- a/test/libc/fmt/test.mk +++ b/test/libc/fmt/test.mk @@ -49,7 +49,7 @@ o/$(MODE)/test/libc/fmt/%.com.dbg: \ o/$(MODE)/test/libc/fmt/fmt.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_FMT_OBJS): test/libc/fmt/test.mk diff --git a/test/libc/intrin/test.mk b/test/libc/intrin/test.mk index c29c57784..77e858ccd 100644 --- a/test/libc/intrin/test.mk +++ b/test/libc/intrin/test.mk @@ -54,7 +54,7 @@ o/$(MODE)/test/libc/intrin/%.com.dbg: \ o/$(MODE)/test/libc/intrin/intrin.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_INTRIN_OBJS): \ diff --git a/test/libc/log/backtrace.c b/test/libc/log/backtrace.c new file mode 100644 index 000000000..857e7d8dd --- /dev/null +++ b/test/libc/log/backtrace.c @@ -0,0 +1,132 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/limits.h" +#include "libc/log/log.h" +#include "libc/runtime/symbols.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" + +int StackOverflow(int f(), int n) { + if (n < INT_MAX) { + return f(f, n + 1) - 1; + } else { + return INT_MAX; + } +} + +void FpuCrash(void) { + typedef char xmm_t __attribute__((__vector_size__(16))); + xmm_t v = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + volatile int x = 0; + asm volatile("fldpi"); + asm volatile("mov\t%0,%%r15" : /* no outputs */ : "g"(0x3133731337)); + asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "x"(v)); + fputc(7 / x, stdout); +} + +char bss[10]; +void BssOverrunCrash(int n) { + int i; + for (i = 0; i < n; ++i) { + bss[i] = i; + } +} + +char data[10] = "abcdeabcde"; +void DataOverrunCrash(int n) { + int i; + for (i = 0; i < n; ++i) { + data[i] = i; + } +} + +const char rodata[10] = "abcdeabcde"; +int RodataOverrunCrash(int i) { + return rodata[i]; +} + +char *StackOverrunCrash(int n) { + int i; + char stack[10]; + bzero(stack, sizeof(stack)); + for (i = 0; i < n; ++i) { + stack[i] = i; + } + return strdup(stack); +} + +char *MemoryLeakCrash(void) { + char *p = strdup("doge"); + CheckForMemoryLeaks(); + return p; +} + +int NpeCrash(char *p) { + asm("nop"); // xxx: due to backtrace addr-1 thing + return *p; +} + +void (*pFpuCrash)(void) = FpuCrash; +void (*pBssOverrunCrash)(int) = BssOverrunCrash; +void (*pDataOverrunCrash)(int) = DataOverrunCrash; +int (*pRodataOverrunCrash)(int) = RodataOverrunCrash; +char *(*pStackOverrunCrash)(int) = StackOverrunCrash; +char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash; +int (*pNpeCrash)(char *) = NpeCrash; +int (*pStackOverflow)(int (*)(), int) = StackOverflow; + +int main(int argc, char *argv[]) { + ShowCrashReports(); + if (argc > 1) { + switch (atoi(argv[1])) { + case 0: + break; + case 1: + pFpuCrash(); + exit(0); + case 2: + pBssOverrunCrash(10 + 1); + exit(0); + case 3: + exit(pRodataOverrunCrash(10 + 1)); + case 4: + pDataOverrunCrash(10 + 1); + exit(0); + case 5: + exit((intptr_t)pStackOverrunCrash(10 + 1)); + case 6: + exit((intptr_t)pMemoryLeakCrash()); + case 7: + exit(pNpeCrash(0)); + case 8: + __cxa_finalize(0); + exit(pNpeCrash(0)); + case 9: + exit(pStackOverflow(pStackOverflow, 0)); + default: + fputs("error: unrecognized argument\n", stderr); + exit(1); + } + } else { + fputs("error: too few args\n", stderr); + exit(1); + } +} diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index a8fd805c9..e313fae7e 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -26,6 +26,7 @@ #include "libc/limits.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" +#include "libc/mem/io.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/internal.h" @@ -40,115 +41,30 @@ #include "libc/x/x.h" #include "net/http/escape.h" -int StackOverflow(int f(), int n) { - if (n < INT_MAX) { - return f(f, n + 1) - 1; - } else { - return INT_MAX; - } +STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("backtrace.com"); +STATIC_YOINK("backtrace.com.dbg"); + +char testlib_enable_tmp_setup_teardown_once; + +void Extract(const char *from, const char *to, int mode) { + ASSERT_SYS(0, 3, open(from, O_RDONLY)); + ASSERT_SYS(0, 4, creat(to, mode)); + ASSERT_NE(-1, _copyfd(3, 4, -1)); + EXPECT_SYS(0, 0, close(4)); + EXPECT_SYS(0, 0, close(3)); +} + +void SetUpOnce(void) { + ASSERT_NE(-1, mkdir("bin", 0755)); + Extract("/zip/backtrace.com", "bin/backtrace.com", 0755); + Extract("/zip/backtrace.com.dbg", "bin/backtrace.com.dbg", 0755); } static bool OutputHasSymbol(const char *output, const char *s) { return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL")); } -void FpuCrash(void) { - typedef char xmm_t __attribute__((__vector_size__(16))); - xmm_t v = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - volatile int x = 0; - asm volatile("fldpi"); - asm volatile("mov\t%0,%%r15" : /* no outputs */ : "g"(0x3133731337)); - asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "x"(v)); - fputc(7 / x, stdout); -} - -char bss[10]; -void BssOverrunCrash(int n) { - int i; - for (i = 0; i < n; ++i) { - bss[i] = i; - } -} - -char data[10] = "abcdeabcde"; -void DataOverrunCrash(int n) { - int i; - for (i = 0; i < n; ++i) { - data[i] = i; - } -} - -const char rodata[10] = "abcdeabcde"; -int RodataOverrunCrash(int i) { - return rodata[i]; -} - -char *StackOverrunCrash(int n) { - int i; - char stack[10]; - bzero(stack, sizeof(stack)); - for (i = 0; i < n; ++i) { - stack[i] = i; - } - return strdup(stack); -} - -char *MemoryLeakCrash(void) { - char *p = strdup("doge"); - CheckForMemoryLeaks(); - return p; -} - -int NpeCrash(char *p) { - asm("nop"); // xxx: due to backtrace addr-1 thing - return *p; -} - -void (*pFpuCrash)(void) = FpuCrash; -void (*pBssOverrunCrash)(int) = BssOverrunCrash; -void (*pDataOverrunCrash)(int) = DataOverrunCrash; -int (*pRodataOverrunCrash)(int) = RodataOverrunCrash; -char *(*pStackOverrunCrash)(int) = StackOverrunCrash; -char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash; -int (*pNpeCrash)(char *) = NpeCrash; -int (*pStackOverflow)(int (*)(), int) = StackOverflow; - -void SetUp(void) { - ShowCrashReports(); - if (__argc == 2) { - switch (atoi(__argv[1])) { - case 0: - break; - case 1: - pFpuCrash(); - exit(0); - case 2: - pBssOverrunCrash(10 + 1); - exit(0); - case 3: - exit(pRodataOverrunCrash(10 + 1)); - case 4: - pDataOverrunCrash(10 + 1); - exit(0); - case 5: - exit((intptr_t)pStackOverrunCrash(10 + 1)); - case 6: - exit((intptr_t)pMemoryLeakCrash()); - case 7: - exit(pNpeCrash(0)); - case 8: - __cxa_finalize(0); - exit(pNpeCrash(0)); - case 9: - exit(pStackOverflow(pStackOverflow, 0)); - default: - printf("preventing fork recursion: %s\n", __argv[1]); - exit(1); - } - } -} - // UNFREED MEMORY // o/dbg/test/libc/log/backtrace_test.com // max allocated space 655,360 @@ -189,9 +105,8 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "6", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "6", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -267,9 +182,8 @@ TEST(ShowCrashReports, testStackOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "5", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "5", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -376,9 +290,8 @@ TEST(ShowCrashReports, testDivideByZero) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "1", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "1", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -454,9 +367,8 @@ TEST(ShowCrashReports, testStackOverflow) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "9", "--strace", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "9", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -571,9 +483,8 @@ TEST(ShowCrashReports, testBssOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "2", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "2", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -650,9 +561,8 @@ TEST(ShowCrashReports, testNpeCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "7", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "7", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -710,9 +620,8 @@ TEST(ShowCrashReports, testDataOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "4", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "4", 0}); + _Exit(127); } close(fds[1]); output = 0; @@ -765,9 +674,8 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(GetProgramExecutableName(), - (char *const[]){GetProgramExecutableName(), "8", 0}); - _exit(127); + execv("bin/backtrace.com", (char *const[]){"bin/backtrace.com", "8", 0}); + _Exit(127); } close(fds[1]); output = 0; diff --git a/test/libc/log/test.mk b/test/libc/log/test.mk index 6a05cf495..34d81864f 100644 --- a/test/libc/log/test.mk +++ b/test/libc/log/test.mk @@ -6,56 +6,82 @@ PKGS += TEST_LIBC_LOG TEST_LIBC_LOG_SRCS := $(wildcard test/libc/log/*.c) TEST_LIBC_LOG_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_LOG_SRCS)) -TEST_LIBC_LOG_OBJS = \ - $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o) +TEST_LIBC_LOG_OBJS = \ + $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o) \ + o/$(MODE)/test/libc/log/backtrace.com.zip.o \ + o/$(MODE)/test/libc/log/backtrace.com.dbg.zip.o -TEST_LIBC_LOG_COMS = \ +TEST_LIBC_LOG_COMS = \ $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.com) -TEST_LIBC_LOG_BINS = \ - $(TEST_LIBC_LOG_COMS) \ +TEST_LIBC_LOG_BINS = \ + $(TEST_LIBC_LOG_COMS) \ $(TEST_LIBC_LOG_COMS:%=%.dbg) -TEST_LIBC_LOG_TESTS = \ +TEST_LIBC_LOG_TESTS = \ $(TEST_LIBC_LOG_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) -TEST_LIBC_LOG_CHECKS = \ +TEST_LIBC_LOG_CHECKS = \ $(TEST_LIBC_LOG_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) -TEST_LIBC_LOG_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_RUNTIME \ - NET_HTTP \ - LIBC_STDIO \ - LIBC_X \ - LIBC_INTRIN \ - LIBC_FMT \ - LIBC_MEM \ - LIBC_NEXGEN32E \ - LIBC_LOG \ - LIBC_STR \ - LIBC_STUBS \ - LIBC_TESTLIB \ - LIBC_SYSV \ - LIBC_LOG +TEST_LIBC_LOG_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_RUNTIME \ + NET_HTTP \ + LIBC_STDIO \ + LIBC_X \ + LIBC_INTRIN \ + LIBC_FMT \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_LOG \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_TESTLIB \ + LIBC_SYSV \ + LIBC_LOG \ + LIBC_ZIPOS -TEST_LIBC_LOG_DEPS := \ +TEST_LIBC_LOG_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_LOG_DIRECTDEPS),$($(x)))) -o/$(MODE)/test/libc/log/log.pkg: \ - $(TEST_LIBC_LOG_OBJS) \ +o/$(MODE)/test/libc/log/log.pkg: \ + $(TEST_LIBC_LOG_OBJS) \ $(foreach x,$(TEST_LIBC_LOG_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/test/libc/log/%.com.dbg: \ - $(TEST_LIBC_LOG_DEPS) \ - o/$(MODE)/test/libc/log/%.o \ - o/$(MODE)/test/libc/log/log.pkg \ - $(LIBC_TESTMAIN) \ - $(CRT) \ - $(APE) +o/$(MODE)/test/libc/log/%.com.dbg: \ + $(TEST_LIBC_LOG_DEPS) \ + o/$(MODE)/test/libc/log/%.o \ + o/$(MODE)/test/libc/log/log.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/test/libc/log/backtrace_test.com.dbg: \ + $(TEST_LIBC_LOG_DEPS) \ + o/$(MODE)/test/libc/log/backtrace.com.zip.o \ + o/$(MODE)/test/libc/log/backtrace.com.dbg.zip.o \ + o/$(MODE)/test/libc/log/backtrace_test.o \ + o/$(MODE)/test/libc/log/log.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/log/backtrace.com.dbg: \ + $(TEST_LIBC_LOG_DEPS) \ + o/$(MODE)/test/libc/log/backtrace.o \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/log/backtrace.com.zip.o \ +o/$(MODE)/test/libc/log/backtrace.com.dbg.zip.o: \ + ZIPOBJ_FLAGS += \ + -B + .PHONY: o/$(MODE)/test/libc/log -o/$(MODE)/test/libc/log: \ - $(TEST_LIBC_LOG_BINS) \ +o/$(MODE)/test/libc/log: \ + $(TEST_LIBC_LOG_BINS) \ $(TEST_LIBC_LOG_CHECKS) diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index cdb4dc420..ced1e6174 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -58,7 +58,7 @@ o/$(MODE)/test/libc/mem/%.com.dbg: \ o/$(MODE)/test/libc/mem/mem.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_MEM_OBJS): \ diff --git a/test/libc/nexgen32e/test.mk b/test/libc/nexgen32e/test.mk index c86b2ad2d..5cc459028 100644 --- a/test/libc/nexgen32e/test.mk +++ b/test/libc/nexgen32e/test.mk @@ -55,7 +55,7 @@ o/$(MODE)/test/libc/nexgen32e/%.com.dbg: \ o/$(MODE)/test/libc/nexgen32e/nexgen32e.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_NEXGEN32E_OBJS): \ diff --git a/test/libc/rand/test.mk b/test/libc/rand/test.mk index a26a99a32..9195d233b 100644 --- a/test/libc/rand/test.mk +++ b/test/libc/rand/test.mk @@ -52,7 +52,7 @@ o/$(MODE)/test/libc/rand/%.com.dbg: \ o/$(MODE)/test/libc/rand/rand.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_RAND_OBJS): test/libc/rand/test.mk diff --git a/test/libc/release/metal.sh b/test/libc/release/metal.sh index 250fcc76c..29c0bae0a 100755 --- a/test/libc/release/metal.sh +++ b/test/libc/release/metal.sh @@ -10,7 +10,7 @@ if [ "$MODE" = opt ] || [ "$MODE" = optlinux ]; then exit fi -mkdir -p o/$MODE/test/libc/release/ +$MKDIR o/$MODE/test/libc/release/ # smoke test booting on bare metal and printing data to serial uart CMD="o/$MODE/tool/build/blinkenlights.com.dbg -r o/$MODE/examples/hello.com" diff --git a/test/libc/release/test.mk b/test/libc/release/test.mk index 70982d4f3..b1edcb485 100644 --- a/test/libc/release/test.mk +++ b/test/libc/release/test.mk @@ -124,17 +124,17 @@ o/$(MODE)/test/libc/release/smokeansi.com.dbg: \ o/$(MODE)/ape/ape.o \ o/$(MODE)/cosmopolitan.a -o/$(MODE)/test/libc/release/metal.ok: \ - test/libc/release/metal.sh \ - o/$(MODE)/examples/hello.com \ - o/$(MODE)/tool/build/blinkenlights.com.dbg - @$(COMPILE) -ASHTEST -tT$@ $< - -o/$(MODE)/test/libc/release/emulate.ok: \ - test/libc/release/emulate.sh \ - o/$(MODE)/examples/hello.com \ - o/$(MODE)/tool/build/blinkenlights.com.dbg - @$(COMPILE) -ASHTEST -tT$@ $< +# TODO(jart): Rewrite these shell scripts as C code. +# o/$(MODE)/test/libc/release/metal.ok: \ +# test/libc/release/metal.sh \ +# o/$(MODE)/examples/hello.com \ +# o/$(MODE)/tool/build/blinkenlights.com.dbg +# @$(COMPILE) -ASHTEST -tT$@ $< +# o/$(MODE)/test/libc/release/emulate.ok: \ +# test/libc/release/emulate.sh \ +# o/$(MODE)/examples/hello.com \ +# o/$(MODE)/tool/build/blinkenlights.com.dbg +# @$(COMPILE) -ASHTEST -tT$@ $< .PHONY: o/$(MODE)/test/libc/release o/$(MODE)/test/libc/release: \ @@ -145,6 +145,4 @@ o/$(MODE)/test/libc/release: \ o/$(MODE)/test/libc/release/smokecxx.com \ o/$(MODE)/test/libc/release/smokecxx.com.runs \ o/$(MODE)/test/libc/release/smokeansi.com \ - o/$(MODE)/test/libc/release/smokeansi.com.runs \ - o/$(MODE)/test/libc/release/emulate.ok \ - o/$(MODE)/test/libc/release/metal.ok + o/$(MODE)/test/libc/release/smokeansi.com.runs diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 285581aa2..15e5da374 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -24,6 +24,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" #include "libc/linux/mmap.h" #include "libc/linux/munmap.h" #include "libc/log/log.h" @@ -228,6 +229,7 @@ TEST(mmap, cow) { char *p; char path[PATH_MAX]; sprintf(path, "%s%s.%ld", kTmpPath, program_invocation_short_name, lemur64()); + kprintf("path = %#s\n", path); ASSERT_NE(-1, (fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644))); EXPECT_EQ(5, write(fd, "hello", 5)); EXPECT_NE(-1, fdatasync(fd)); diff --git a/test/libc/runtime/test.mk b/test/libc/runtime/test.mk index c4881f68a..05c56c00f 100644 --- a/test/libc/runtime/test.mk +++ b/test/libc/runtime/test.mk @@ -58,7 +58,7 @@ o/$(MODE)/test/libc/runtime/%.com.dbg: \ o/$(MODE)/test/libc/runtime/runtime.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/test/libc/runtime/ape_test.com.dbg: \ @@ -67,7 +67,7 @@ o/$(MODE)/test/libc/runtime/ape_test.com.dbg: \ o/$(MODE)/test/libc/runtime/runtime.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_RUNTIME_OBJS): \ diff --git a/test/libc/sock/test.mk b/test/libc/sock/test.mk index 961a53c4a..375d3e4fc 100644 --- a/test/libc/sock/test.mk +++ b/test/libc/sock/test.mk @@ -51,7 +51,7 @@ o/$(MODE)/test/libc/sock/%.com.dbg: \ o/$(MODE)/test/libc/sock/sock.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_SOCK_OBJS): test/libc/sock/test.mk diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index 468f42497..8eb805252 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -57,7 +57,7 @@ o/$(MODE)/test/libc/stdio/%.com.dbg: \ o/$(MODE)/test/libc/stdio/stdio.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_STDIO_OBJS): \ diff --git a/test/libc/str/test.mk b/test/libc/str/test.mk index 7b3ec9f89..1bcd04d7e 100644 --- a/test/libc/str/test.mk +++ b/test/libc/str/test.mk @@ -72,7 +72,7 @@ o/$(MODE)/test/libc/str/%.com.dbg: \ o/$(MODE)/test/libc/str/str.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/test/libc/str/blake2.com.dbg: \ @@ -82,7 +82,7 @@ o/$(MODE)/test/libc/str/blake2.com.dbg: \ o/$(MODE)/test/libc/str/str.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_STR_OBJS): \ diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk index 33a46bc08..c62a0429a 100644 --- a/test/libc/thread/test.mk +++ b/test/libc/thread/test.mk @@ -48,7 +48,7 @@ o/$(MODE)/test/libc/thread/%.com.dbg: \ o/$(MODE)/test/libc/thread/thread.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_THREAD_OBJS): \ diff --git a/test/libc/time/test.mk b/test/libc/time/test.mk index 4744b92d2..caa61cba9 100644 --- a/test/libc/time/test.mk +++ b/test/libc/time/test.mk @@ -43,7 +43,7 @@ o/$(MODE)/test/libc/time/%.com.dbg: \ o/$(MODE)/test/libc/time/time.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/libc/time diff --git a/test/libc/tinymath/test.mk b/test/libc/tinymath/test.mk index 4feea7a30..931e7e706 100644 --- a/test/libc/tinymath/test.mk +++ b/test/libc/tinymath/test.mk @@ -53,7 +53,7 @@ o/$(MODE)/test/libc/tinymath/%.com.dbg: \ o/$(MODE)/test/libc/tinymath/tinymath.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TEST_LIBC_TINYMATH_OBJS): \ diff --git a/test/libc/unicode/test.mk b/test/libc/unicode/test.mk index 2b49b9c52..85ecba50c 100644 --- a/test/libc/unicode/test.mk +++ b/test/libc/unicode/test.mk @@ -47,7 +47,7 @@ o/$(MODE)/test/libc/unicode/%.com.dbg: \ o/$(MODE)/test/libc/unicode/unicode.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/libc/unicode diff --git a/test/libc/x/test.mk b/test/libc/x/test.mk index 025c759c0..ef812823c 100644 --- a/test/libc/x/test.mk +++ b/test/libc/x/test.mk @@ -53,7 +53,7 @@ o/$(MODE)/test/libc/x/%.com.dbg: \ o/$(MODE)/test/libc/x/x.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/libc/x diff --git a/test/libc/x/xfixpath_test.c b/test/libc/x/xfixpath_test.c new file mode 100644 index 000000000..63a5a6b2d --- /dev/null +++ b/test/libc/x/xfixpath_test.c @@ -0,0 +1,27 @@ +/*-*- 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/runtime/runtime.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(xfixpath, test) { + setenv("PATH", "C:\\bin;C:\\usr\\bin;C:\\usr\\local\\bin", true); + xfixpath(); + ASSERT_STREQ("/C/bin:/C/usr/bin:/C/usr/local/bin", getenv("PATH")); +} diff --git a/test/libc/xed/test.mk b/test/libc/xed/test.mk index e51d88fd3..440f93ec9 100644 --- a/test/libc/xed/test.mk +++ b/test/libc/xed/test.mk @@ -93,7 +93,7 @@ o/$(MODE)/test/libc/xed/%.com.dbg: \ o/$(MODE)/test/libc/xed/xed.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) #─────────────────────────────────────────────────────────────────────────────── diff --git a/test/net/http/test.mk b/test/net/http/test.mk index c07da4234..e9d4b54db 100644 --- a/test/net/http/test.mk +++ b/test/net/http/test.mk @@ -37,7 +37,7 @@ o/$(MODE)/test/net/http/%.com.dbg: \ o/$(MODE)/test/net/http/%.o \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/net/http diff --git a/test/net/https/test.mk b/test/net/https/test.mk index be8db89a6..0970a3577 100644 --- a/test/net/https/test.mk +++ b/test/net/https/test.mk @@ -38,7 +38,7 @@ o/$(MODE)/test/net/https/%.com.dbg: \ o/$(MODE)/test/net/https/%.o \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/net/https diff --git a/test/tool/args/test.mk b/test/tool/args/test.mk index ff9019a44..91bd5eca5 100644 --- a/test/tool/args/test.mk +++ b/test/tool/args/test.mk @@ -66,7 +66,7 @@ o/$(MODE)/test/tool/args/%.com.dbg: \ $(TEST_TOOL_ARGS_A).pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/tool/args diff --git a/test/tool/build/lib/test.mk b/test/tool/build/lib/test.mk index dda3332c2..28ffd7bee 100644 --- a/test/tool/build/lib/test.mk +++ b/test/tool/build/lib/test.mk @@ -66,7 +66,7 @@ o/$(MODE)/test/tool/build/lib/%.com.dbg: \ $(TEST_TOOL_BUILD_LIB_A).pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/tool/build/lib diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index 3445d799b..c652a90a0 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -68,7 +68,7 @@ o/$(MODE)/test/tool/net/%.com.dbg: \ $(TEST_TOOL_NET_A).pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/tool/net diff --git a/test/tool/plinko/test.mk b/test/tool/plinko/test.mk index c247d780a..f93b5a9e2 100644 --- a/test/tool/plinko/test.mk +++ b/test/tool/plinko/test.mk @@ -74,7 +74,7 @@ o/$(MODE)/test/tool/plinko/%.com.dbg: \ $(TEST_TOOL_PLINKO_A).pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/test/tool/plinko/plinko_test.com.runs: \ diff --git a/test/tool/viz/lib/test.mk b/test/tool/viz/lib/test.mk index 8aa21f4fd..ca85dda17 100644 --- a/test/tool/viz/lib/test.mk +++ b/test/tool/viz/lib/test.mk @@ -55,7 +55,7 @@ o/$(MODE)/test/tool/viz/lib/%.com.dbg: \ o/$(MODE)/test/tool/viz/lib/vizlib.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/tool/viz/lib diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c index e1fff9230..c189381d5 100644 --- a/third_party/chibicc/chibicc.c +++ b/third_party/chibicc/chibicc.c @@ -80,7 +80,7 @@ static void chibicc_usage(int status) { char *p; size_t n; p = xslurp("/zip/third_party/chibicc/help.txt", &n); - xwrite(1, p, n); + __paginate(1, p); _Exit(status); } @@ -447,7 +447,8 @@ static bool run_subprocess(char **argv) { _Exit(1); } // Wait for the child process to finish. - do rc = wait(&ws); + do + rc = wait(&ws); while (rc == -1 && errno == EINTR); return WIFEXITED(ws) && WEXITSTATUS(ws) == 0; } diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index ec387ba89..892379df5 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -93,7 +93,7 @@ $(THIRD_PARTY_CHIBICC2_A).pkg: \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg: \ $(THIRD_PARTY_CHIBICC_A_DEPS) \ $(THIRD_PARTY_CHIBICC_A) \ - $(APE) \ + $(APE_NO_MODIFY_SELF) \ $(CRT) \ o/$(MODE)/third_party/chibicc/help.txt.zip.o \ o/$(MODE)/third_party/chibicc/chibicc.main.o \ @@ -102,7 +102,7 @@ o/$(MODE)/third_party/chibicc/chibicc.com.dbg: \ o/$(MODE)/third_party/chibicc/chibicc2.com.dbg: \ $(THIRD_PARTY_CHIBICC_A_DEPS) \ $(THIRD_PARTY_CHIBICC2_A) \ - $(APE) \ + $(APE_NO_MODIFY_SELF) \ $(CRT) \ o/$(MODE)/third_party/chibicc/help.txt.zip.o \ o/$(MODE)/third_party/chibicc/chibicc.main.chibicc.o \ @@ -122,7 +122,7 @@ o/$(MODE)/third_party/chibicc/chibicc.com: \ o/$(MODE)/third_party/chibicc/as.com.dbg: \ $(THIRD_PARTY_CHIBICC_A_DEPS) \ $(THIRD_PARTY_CHIBICC_A) \ - $(APE) \ + $(APE_NO_MODIFY_SELF) \ $(CRT) \ o/$(MODE)/third_party/chibicc/as.main.o \ $(THIRD_PARTY_CHIBICC_A).pkg diff --git a/third_party/chibicc/help.txt b/third_party/chibicc/help.txt index 2d5bba64b..1e2730726 100644 --- a/third_party/chibicc/help.txt +++ b/third_party/chibicc/help.txt @@ -259,6 +259,15 @@ BUILTIN FUNCTIONS double __builtin_copysign(double, double) float __builtin_copysignf(float, float) long double __builtin_copysignl(long double, long double) + void __builtin_ia32_movntq(di *, di) + int __builtin_ia32_pmovmskb128(v16qi) + T __atomic_load(T *addr, int memorder) + void __atomic_clear(_Bool *addr, int memorder) + _Bool __atomic_test_and_set(void *addr, int memorder) + T __atomic_sub_fetch(T *addr, T amt, int memorder) + T __atomic_fetch_add(T *addr, T amt, int memorder) + T __sync_lock_test_and_set(T *addr, T value, ...) + void __sync_lock_release(T *addr, ...) BUILTIN OBJECTS diff --git a/third_party/chibicc/test/test.mk b/third_party/chibicc/test/test.mk index e42be50b0..3686e6db3 100644 --- a/third_party/chibicc/test/test.mk +++ b/third_party/chibicc/test/test.mk @@ -79,7 +79,7 @@ o/$(MODE)/third_party/chibicc/test/%.com.dbg: \ o/$(MODE)/third_party/chibicc/test/%.chibicc.o \ $(THIRD_PARTY_CHIBICC_TEST_A).pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/chibicc/test/%2.com.dbg: \ @@ -88,7 +88,7 @@ o/$(MODE)/third_party/chibicc/test/%2.com.dbg: \ o/$(MODE)/third_party/chibicc/test/%.chibicc2.o \ $(THIRD_PARTY_CHIBICC_TEST2_A).pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PRECIOUS: $(THIRD_PARTY_CHIBICC_TEST_OBJS) diff --git a/third_party/gcc/bin/x86_64-linux-musl-c++ b/third_party/gcc/bin/x86_64-linux-musl-c++ deleted file mode 120000 index 5311ab2a9..000000000 --- a/third_party/gcc/bin/x86_64-linux-musl-c++ +++ /dev/null @@ -1 +0,0 @@ -x86_64-linux-musl-g++ \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-c++.sym b/third_party/gcc/bin/x86_64-linux-musl-c++.sym new file mode 100644 index 000000000..5311ab2a9 --- /dev/null +++ b/third_party/gcc/bin/x86_64-linux-musl-c++.sym @@ -0,0 +1 @@ +x86_64-linux-musl-g++ \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-cc b/third_party/gcc/bin/x86_64-linux-musl-cc deleted file mode 120000 index d7bd3b204..000000000 --- a/third_party/gcc/bin/x86_64-linux-musl-cc +++ /dev/null @@ -1 +0,0 @@ -x86_64-linux-musl-gcc \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-cc.sym b/third_party/gcc/bin/x86_64-linux-musl-cc.sym new file mode 100644 index 000000000..d7bd3b204 --- /dev/null +++ b/third_party/gcc/bin/x86_64-linux-musl-cc.sym @@ -0,0 +1 @@ +x86_64-linux-musl-gcc \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0 b/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0 deleted file mode 120000 index d7bd3b204..000000000 --- a/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0 +++ /dev/null @@ -1 +0,0 @@ -x86_64-linux-musl-gcc \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0.sym b/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0.sym new file mode 100644 index 000000000..d7bd3b204 --- /dev/null +++ b/third_party/gcc/bin/x86_64-linux-musl-gcc-9.2.0.sym @@ -0,0 +1 @@ +x86_64-linux-musl-gcc \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-ld b/third_party/gcc/bin/x86_64-linux-musl-ld deleted file mode 120000 index cf399b994..000000000 --- a/third_party/gcc/bin/x86_64-linux-musl-ld +++ /dev/null @@ -1 +0,0 @@ -../x86_64-linux-musl/bin/ld.bfd \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-ld.bfd b/third_party/gcc/bin/x86_64-linux-musl-ld.bfd deleted file mode 120000 index cf399b994..000000000 --- a/third_party/gcc/bin/x86_64-linux-musl-ld.bfd +++ /dev/null @@ -1 +0,0 @@ -../x86_64-linux-musl/bin/ld.bfd \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-ld.bfd.sym b/third_party/gcc/bin/x86_64-linux-musl-ld.bfd.sym new file mode 100644 index 000000000..cf399b994 --- /dev/null +++ b/third_party/gcc/bin/x86_64-linux-musl-ld.bfd.sym @@ -0,0 +1 @@ +../x86_64-linux-musl/bin/ld.bfd \ No newline at end of file diff --git a/third_party/gcc/bin/x86_64-linux-musl-ld.sym b/third_party/gcc/bin/x86_64-linux-musl-ld.sym new file mode 100644 index 000000000..cf399b994 --- /dev/null +++ b/third_party/gcc/bin/x86_64-linux-musl-ld.sym @@ -0,0 +1 @@ +../x86_64-linux-musl/bin/ld.bfd \ No newline at end of file diff --git a/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as b/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as deleted file mode 120000 index 244777532..000000000 --- a/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as +++ /dev/null @@ -1 +0,0 @@ -../../../../bin/x86_64-linux-musl-as \ No newline at end of file diff --git a/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as.sym b/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as.sym new file mode 100644 index 000000000..244777532 --- /dev/null +++ b/third_party/gcc/libexec/gcc/x86_64-linux-musl/9.2.0/as.sym @@ -0,0 +1 @@ +../../../../bin/x86_64-linux-musl-as \ No newline at end of file diff --git a/third_party/gcc/unbundle.sh b/third_party/gcc/unbundle.sh deleted file mode 100755 index 77bfb0168..000000000 --- a/third_party/gcc/unbundle.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐ -#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘ -mkdir -p o/third_party -cp -R third_party/gcc o/third_party/gcc.$$ -for f in $(find o/third_party/gcc.$$ -name \*.gz); do - gunzip $f - chmod +x ${f%.gz} -done -mv o/third_party/gcc.$$ o/third_party/gcc diff --git a/third_party/gcc/x86_64-linux-musl/bin/ar b/third_party/gcc/x86_64-linux-musl/bin/ar deleted file mode 120000 index 18b47c53d..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/ar +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-ar \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/ar.sym b/third_party/gcc/x86_64-linux-musl/bin/ar.sym new file mode 100644 index 000000000..18b47c53d --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/ar.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-ar \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/as b/third_party/gcc/x86_64-linux-musl/bin/as deleted file mode 120000 index 9db7664e8..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/as +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-as \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/as.sym b/third_party/gcc/x86_64-linux-musl/bin/as.sym new file mode 100644 index 000000000..9db7664e8 --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/as.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-as \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/ld b/third_party/gcc/x86_64-linux-musl/bin/ld deleted file mode 120000 index 3f5ce09ea..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/ld +++ /dev/null @@ -1 +0,0 @@ -ld.bfd \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/ld.sym b/third_party/gcc/x86_64-linux-musl/bin/ld.sym new file mode 100644 index 000000000..3f5ce09ea --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/ld.sym @@ -0,0 +1 @@ +ld.bfd \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/nm b/third_party/gcc/x86_64-linux-musl/bin/nm deleted file mode 120000 index 8312c6cf2..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/nm +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-nm \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/nm.sym b/third_party/gcc/x86_64-linux-musl/bin/nm.sym new file mode 100644 index 000000000..8312c6cf2 --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/nm.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-nm \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/objcopy b/third_party/gcc/x86_64-linux-musl/bin/objcopy deleted file mode 120000 index 1996c8fbd..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/objcopy +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-objcopy \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/objcopy.sym b/third_party/gcc/x86_64-linux-musl/bin/objcopy.sym new file mode 100644 index 000000000..1996c8fbd --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/objcopy.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-objcopy \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/objdump b/third_party/gcc/x86_64-linux-musl/bin/objdump deleted file mode 120000 index 9e6eb648e..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/objdump +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-objdump \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/objdump.sym b/third_party/gcc/x86_64-linux-musl/bin/objdump.sym new file mode 100644 index 000000000..9e6eb648e --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/objdump.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-objdump \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/ranlib b/third_party/gcc/x86_64-linux-musl/bin/ranlib deleted file mode 120000 index 9fd9a9ba5..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/ranlib +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-ranlib \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/ranlib.sym b/third_party/gcc/x86_64-linux-musl/bin/ranlib.sym new file mode 100644 index 000000000..9fd9a9ba5 --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/ranlib.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-ranlib \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/readelf b/third_party/gcc/x86_64-linux-musl/bin/readelf deleted file mode 120000 index bce53096b..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/readelf +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-readelf \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/readelf.sym b/third_party/gcc/x86_64-linux-musl/bin/readelf.sym new file mode 100644 index 000000000..bce53096b --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/readelf.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-readelf \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/strip b/third_party/gcc/x86_64-linux-musl/bin/strip deleted file mode 120000 index 3905fc078..000000000 --- a/third_party/gcc/x86_64-linux-musl/bin/strip +++ /dev/null @@ -1 +0,0 @@ -../../bin/x86_64-linux-musl-strip \ No newline at end of file diff --git a/third_party/gcc/x86_64-linux-musl/bin/strip.sym b/third_party/gcc/x86_64-linux-musl/bin/strip.sym new file mode 100644 index 000000000..3905fc078 --- /dev/null +++ b/third_party/gcc/x86_64-linux-musl/bin/strip.sym @@ -0,0 +1 @@ +../../bin/x86_64-linux-musl-strip \ No newline at end of file diff --git a/third_party/mbedtls/test/test.mk b/third_party/mbedtls/test/test.mk index 29130433e..95df2b1b1 100644 --- a/third_party/mbedtls/test/test.mk +++ b/third_party/mbedtls/test/test.mk @@ -130,7 +130,7 @@ o/$(MODE)/third_party/mbedtls/test/%.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/%.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/%.com: o/$(MODE)/third_party/mbedtls/test/%.com.dbg @@ -165,7 +165,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cbc.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cbc.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cfb.com: o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cfb.com.dbg @@ -176,7 +176,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cfb.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.cfb.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ecb.com: o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ecb.com.dbg @@ -187,7 +187,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ecb.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ecb.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ofb.com: o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ofb.com.dbg @@ -198,7 +198,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ofb.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.ofb.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_aes.rest.com: o/$(MODE)/third_party/mbedtls/test/test_suite_aes.rest.com.dbg @@ -209,7 +209,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.rest.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.rest.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_aes.xts.com: o/$(MODE)/third_party/mbedtls/test/test_suite_aes.xts.com.dbg @@ -220,7 +220,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.xts.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_aes.xts.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.com: o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.com.dbg @@ -231,7 +231,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_asn1write.com: o/$(MODE)/third_party/mbedtls/test/test_suite_asn1write.com.dbg @@ -242,7 +242,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_asn1write.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_asn1write.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_base64.com: o/$(MODE)/third_party/mbedtls/test/test_suite_base64.com.dbg @@ -253,7 +253,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_base64.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_base64.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_blowfish.com: o/$(MODE)/third_party/mbedtls/test/test_suite_blowfish.com.dbg @@ -264,7 +264,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_blowfish.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_blowfish.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_chacha20.com: o/$(MODE)/third_party/mbedtls/test/test_suite_chacha20.com.dbg @@ -275,7 +275,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_chacha20.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_chacha20.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_chachapoly.com: o/$(MODE)/third_party/mbedtls/test/test_suite_chachapoly.com.dbg @@ -286,7 +286,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_chachapoly.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_chachapoly.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.aes.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.aes.com.dbg @@ -297,7 +297,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.aes.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.aes.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.blowfish.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.blowfish.com.dbg @@ -308,7 +308,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.blowfish.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.blowfish.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.ccm.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.ccm.com.dbg @@ -319,7 +319,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.ccm.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.ccm.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chacha20.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chacha20.com.dbg @@ -330,7 +330,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chacha20.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chacha20.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chachapoly.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chachapoly.com.dbg @@ -341,7 +341,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chachapoly.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.chachapoly.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.des.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.des.com.dbg @@ -352,7 +352,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.des.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.des.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.gcm.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.gcm.com.dbg @@ -363,7 +363,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.gcm.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.gcm.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.misc.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.misc.com.dbg @@ -374,7 +374,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.misc.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.misc.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.nist_kw.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.nist_kw.com.dbg @@ -385,7 +385,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.nist_kw.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.nist_kw.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.null.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.null.com.dbg @@ -396,7 +396,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.null.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.null.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.padding.com: o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.padding.com.dbg @@ -407,7 +407,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.padding.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_cipher.padding.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_ctr_drbg.com: o/$(MODE)/third_party/mbedtls/test/test_suite_ctr_drbg.com.dbg @@ -418,7 +418,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_ctr_drbg.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_ctr_drbg.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_des.com: o/$(MODE)/third_party/mbedtls/test/test_suite_des.com.dbg @@ -429,7 +429,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_des.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_des.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_dhm.com: o/$(MODE)/third_party/mbedtls/test/test_suite_dhm.com.dbg @@ -442,7 +442,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_dhm.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/dhparams.pem.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_ecdh.com: o/$(MODE)/third_party/mbedtls/test/test_suite_ecdh.com.dbg @@ -453,7 +453,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_ecdh.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_ecdh.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_ecdsa.com: o/$(MODE)/third_party/mbedtls/test/test_suite_ecdsa.com.dbg @@ -464,7 +464,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_ecdsa.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_ecdsa.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_ecp.com: o/$(MODE)/third_party/mbedtls/test/test_suite_ecp.com.dbg @@ -475,7 +475,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_ecp.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_ecp.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_entropy.com: o/$(MODE)/third_party/mbedtls/test/test_suite_entropy.com.dbg @@ -486,7 +486,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_entropy.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_entropy.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_error.com: o/$(MODE)/third_party/mbedtls/test/test_suite_error.com.dbg @@ -497,7 +497,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_error.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_error.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_de.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_de.com.dbg @@ -508,7 +508,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_de.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_de.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_en.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_en.com.dbg @@ -519,7 +519,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_en.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes128_en.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_de.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_de.com.dbg @@ -530,7 +530,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_de.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_de.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_en.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_en.com.dbg @@ -541,7 +541,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_en.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes192_en.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_de.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_de.com.dbg @@ -552,7 +552,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_de.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_de.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_en.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_en.com.dbg @@ -563,7 +563,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_en.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.aes256_en.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.misc.com: o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.misc.com.dbg @@ -574,7 +574,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.misc.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_gcm.misc.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_hkdf.com: o/$(MODE)/third_party/mbedtls/test/test_suite_hkdf.com.dbg @@ -585,7 +585,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_hkdf.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_hkdf.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.misc.com: o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.misc.com.dbg @@ -596,7 +596,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.misc.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.misc.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.no_reseed.com: o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.no_reseed.com.dbg @@ -607,7 +607,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.no_reseed.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.no_reseed.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.nopr.com: o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.nopr.com.dbg @@ -618,7 +618,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.nopr.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.nopr.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.pr.com: o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.pr.com.dbg @@ -629,7 +629,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.pr.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_hmac_drbg.pr.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_md.com: o/$(MODE)/third_party/mbedtls/test/test_suite_md.com.dbg @@ -645,7 +645,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_md.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/hash_file_5.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_mdx.com: o/$(MODE)/third_party/mbedtls/test/test_suite_mdx.com.dbg @@ -656,7 +656,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_mdx.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_mdx.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_memory_buffer_alloc.com: o/$(MODE)/third_party/mbedtls/test/test_suite_memory_buffer_alloc.com.dbg @@ -667,7 +667,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_memory_buffer_alloc.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_memory_buffer_alloc.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_mpi.com: o/$(MODE)/third_party/mbedtls/test/test_suite_mpi.com.dbg @@ -683,7 +683,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_mpi.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/mpi_write.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_net.com: o/$(MODE)/third_party/mbedtls/test/test_suite_net.com.dbg @@ -694,7 +694,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_net.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_net.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_nist_kw.com: o/$(MODE)/third_party/mbedtls/test/test_suite_nist_kw.com.dbg @@ -705,7 +705,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_nist_kw.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_nist_kw.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_oid.com: o/$(MODE)/third_party/mbedtls/test/test_suite_oid.com.dbg @@ -716,7 +716,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_oid.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_oid.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pem.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pem.com.dbg @@ -727,7 +727,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pem.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_pem.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pk.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pk.com.dbg @@ -744,7 +744,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pk.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/server5.key.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v15.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v15.com.dbg @@ -755,7 +755,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v15.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v15.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v21.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v21.com.dbg @@ -766,7 +766,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v21.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs1_v21.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs5.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs5.com.dbg @@ -777,7 +777,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs5.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_pkcs5.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pkparse.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pkparse.com.dbg @@ -917,7 +917,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pkparse.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/test-ca.key.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_pkwrite.com: o/$(MODE)/third_party/mbedtls/test/test_suite_pkwrite.com.dbg @@ -940,7 +940,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_pkwrite.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/server1.pubkey.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_poly1305.com: o/$(MODE)/third_party/mbedtls/test/test_suite_poly1305.com.dbg @@ -951,7 +951,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_poly1305.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_poly1305.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_random.com: o/$(MODE)/third_party/mbedtls/test/test_suite_random.com.dbg @@ -962,7 +962,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_random.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_random.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_rsa.com: o/$(MODE)/third_party/mbedtls/test/test_suite_rsa.com.dbg @@ -973,7 +973,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_rsa.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_rsa.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_shax.com: o/$(MODE)/third_party/mbedtls/test/test_suite_shax.com.dbg @@ -984,7 +984,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_shax.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_shax.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_ssl.com: o/$(MODE)/third_party/mbedtls/test/test_suite_ssl.com.dbg @@ -996,7 +996,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_ssl.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/server5.crt.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_timing.com: o/$(MODE)/third_party/mbedtls/test/test_suite_timing.com.dbg @@ -1007,7 +1007,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_timing.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_timing.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_version.com: o/$(MODE)/third_party/mbedtls/test/test_suite_version.com.dbg @@ -1018,7 +1018,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_version.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test_suite_version.datax.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_x509parse.com: o/$(MODE)/third_party/mbedtls/test/test_suite_x509parse.com.dbg @@ -1303,7 +1303,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_x509parse.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/test-int-ca3.crt.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_x509write.com: o/$(MODE)/third_party/mbedtls/test/test_suite_x509write.com.dbg @@ -1339,7 +1339,7 @@ o/$(MODE)/third_party/mbedtls/test/test_suite_x509write.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/data/test-ca.key.zip.o \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/everest_test.com: o/$(MODE)/third_party/mbedtls/test/everest_test.com.dbg @@ -1350,7 +1350,7 @@ o/$(MODE)/third_party/mbedtls/test/everest_test.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/secp384r1_test.com: o/$(MODE)/third_party/mbedtls/test/secp384r1_test.com.dbg @@ -1360,7 +1360,7 @@ o/$(MODE)/third_party/mbedtls/test/secp384r1_test.com.dbg: \ o/$(MODE)/third_party/mbedtls/test/test.pkg \ $(LIBC_TESTMAIN) \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.com.runs: QUOTA = -M512m diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 412fbae67..a623b04a8 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -2111,1495 +2111,1495 @@ o/$(MODE)/third_party/python/pythontester.com.dbg: \ $(THIRD_PARTY_PYTHON_PYTEST_TODOS:%.py=o/$(MODE)/%.o) \ o/$(MODE)/third_party/python/pythontester.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/python/Lib/test/test_grammar.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_grammar $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_set.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_set $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_genexps.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_genexps $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sqlite.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sqlite $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bz2.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bz2 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_zlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_zlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_opcodes.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_opcodes $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_marshal.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_marshal $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pow.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pow $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_binascii.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_binascii $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_binhex.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_binhex $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_capi.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_capi $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test__locale.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test__locale $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_binop.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_binop $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test___future__.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test___future__ $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test__opcode.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test__opcode $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_abc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_abc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bytes.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bytes $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_setcomps.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_setcomps $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_itertools.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_itertools $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_listcomps.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_listcomps $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_aifc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_aifc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_audioop.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_audioop $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bool.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bool $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_base64.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_base64 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_baseexception.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_baseexception $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_array.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_array $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_builtin.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_builtin $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_charmapcodec.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_charmapcodec $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecs.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecs $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codeop.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codeop $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cgi.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cgi $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_abstract_numbers.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_abstract_numbers $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_augassign.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_augassign $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bigaddrspace.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bigaddrspace $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_class.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_class $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_call.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_call $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_buffer.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_buffer $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bufio.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bufio $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_enum.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_enum $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_code.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_code $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cmd.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cmd $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pwd.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pwd $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cmath.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cmath $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_defaultdict.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_defaultdict $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_decorators.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_decorators $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_copy.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_copy $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_csv.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_csv $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_difflib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_difflib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_colorsys.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_colorsys $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_compare.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_compare $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_copyreg.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_copyreg $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_collections.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_collections $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_format.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_format $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fractions.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fractions $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_eof.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_eof $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fnmatch.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fnmatch $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_frame.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_frame $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dummy_threading.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dummy_threading $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dynamic.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dynamic $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dict.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dict $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_wsgiref.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_wsgiref $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_wave.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_wave $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urlparse.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urlparse $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_userdict.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_userdict $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_userlist.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_userlist $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_userstring.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_userstring $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_utf8source.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_utf8source $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_uu.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_uu $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test__encoded_words.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test__encoded_words $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test__header_value_parser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test__header_value_parser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_asian_codecs.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_asian_codecs $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_contentmanager.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_contentmanager $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_defect_handling.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_defect_handling $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_email.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_email $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_generator.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_generator $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_headerregistry.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_headerregistry $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_inversion.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_inversion $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_message.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_message $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_parser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_parser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_pickleable.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_pickleable $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_policy.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_policy $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_email/test_utils.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_email.test_utils $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_strtod.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_strtod $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_struct.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_struct $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_structmembers.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_structmembers $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_hash.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_hash $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_heapq.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_heapq $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_operator.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_operator $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_optparse.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_optparse $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_finalization.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_finalization $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_enumerate.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_enumerate $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_errno.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_errno $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_html.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_html $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_htmlparser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_htmlparser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_http_cookiejar.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_http_cookiejar $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_http_cookies.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_http_cookies $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_list.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_list $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_long.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_long $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_longexp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_longexp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_glob.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_glob $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_global.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_global $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ipaddress.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ipaddress $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_isinstance.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_isinstance $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_iter.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_iter $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_json/__main__.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_json $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_tarfile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_tarfile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_iterlen.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_iterlen $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_stat.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_stat $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_memoryio.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_memoryio $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_memoryview.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_memoryview $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_metaclass.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_metaclass $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_mimetypes.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_mimetypes $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_kdf.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_kdf $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cosmo.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cosmo $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_scratch.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_scratch $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_hashlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_hashlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_complex.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_complex $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_funcattrs.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_funcattrs $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_functools.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_functools $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_int.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_int $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_int_literal.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_int_literal $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bisect.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bisect $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pyexpat.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pyexpat $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ioctl.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ioctl $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_getopt.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_getopt $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sort.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sort $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_slice.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_slice $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_decimal.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_decimal $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_deque.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_deque $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_mmap.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_mmap $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_poll.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_poll $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_robotparser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_robotparser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_re.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_re $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_range.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_range $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sax.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sax $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_scope.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_scope $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_stringprep.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_stringprep $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_syntax.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_syntax $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unicodedata.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unicodedata $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unpack.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unpack $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unpack_ex.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unpack_ex $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_file.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_file $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_uuid.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_uuid $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_filecmp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_filecmp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fileinput.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fileinput $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fileio.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fileio $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_float.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_float $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pickle.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pickle $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pickletools.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pickletools $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_tuple.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_tuple $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_reprlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_reprlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_strftime.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_strftime $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_quopri.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_quopri $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_with.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_with $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_raise.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_raise $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_yield_from.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_yield_from $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_typechecks.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_typechecks $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_types.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_types $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_random.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_random $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_typing.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_typing $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unary.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unary $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_print.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_print $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pprint.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pprint $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_secrets.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_secrets $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_select.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_select $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_selectors.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_selectors $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_contains.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_contains $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_super.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_super $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unicode.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unicode $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unicode_file.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unicode_file $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unicode_identifiers.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unicode_identifiers $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unicode_file_functions.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unicode_file_functions $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_textwrap.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_textwrap $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pulldom.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pulldom $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_minidom.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_minidom $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_xml_dom_minicompat.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_xml_dom_minicompat $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_xml_etree_c.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_xml_etree_c $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_coroutines.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_coroutines $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_tempfile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_tempfile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_normalization.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_normalization $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_os.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_os $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_logging.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_logging $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_io.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_io $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_gzip.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_gzip $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_tracemalloc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_tracemalloc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_configparser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_configparser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_flufl.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_flufl $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_keyword.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_keyword $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_keywordonlyarg.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_keywordonlyarg $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sys.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sys $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cgitb.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cgitb $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_asyncgen.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_asyncgen $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_runpy.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_runpy $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_doctest.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_doctest $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_doctest2.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_doctest2 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_calendar.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_calendar $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_asynchat.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_asynchat $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_asdl_parser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_asdl_parser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_atexit.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_atexit $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dis.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dis $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_asyncore.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_asyncore $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_epoll.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_epoll $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cmd_line.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cmd_line $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cmd_line_script.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cmd_line_script $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_code_module.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_code_module $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codeccallbacks.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codeccallbacks $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecmaps_cn.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecmaps_cn $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecmaps_jp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecmaps_jp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_cn.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_cn $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_hk.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_hk $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecmaps_hk.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecmaps_hk $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecmaps_kr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecmaps_kr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecmaps_tw.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecmaps_tw $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_iso2022.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_iso2022 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_jp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_jp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_kr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_kr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_codecencodings_tw.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_codecencodings_tw $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_compile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_compile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_contextlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_contextlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_cprofile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_cprofile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_crashers.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_crashers $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_crypt.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_crypt $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_datetime.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_datetime $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_descrtut.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_descrtut $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_devpoll.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_devpoll $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dict_version.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dict_version $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dictcomps.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dictcomps $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dictviews.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dictviews $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dtrace.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dtrace $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_dynamicclassattribute.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_dynamicclassattribute $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_eintr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_eintr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_exception_hierarchy.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_exception_hierarchy $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_xmlrpc_net.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_xmlrpc_net $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_bigmem.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_bigmem $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_exception_variations.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_exception_variations $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_exceptions.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_exceptions $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_time.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_time $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_docxmlrpc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_docxmlrpc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_extcall.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_extcall $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_faulthandler.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_faulthandler $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fcntl.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fcntl $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_file_eintr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_file_eintr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fork1.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fork1 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_fstring.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_fstring $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ftplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ftplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_future.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_future $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_future3.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_future3 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_future4.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_future4 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_future5.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_future5 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_gc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_gc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_gdb.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_gdb $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_generator_stop.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_generator_stop $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_generators.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_generators $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_genericpath.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_genericpath $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_getargs2.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_getargs2 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_getpass.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_getpass $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_gettext.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_gettext $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_grp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_grp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_imaplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_imaplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_imghdr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_imghdr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_imp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_imp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_index.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_index $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_kqueue.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_kqueue $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_largefile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_largefile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_linecache.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_linecache $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_locale.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_locale $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_macpath.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_macpath $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_macurl2path.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_macurl2path $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_mailbox.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_mailbox $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_mailcap.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_mailcap $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_module.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_module $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_modulefinder.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_modulefinder $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_multibytecodec.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_multibytecodec $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_multiprocessing_fork.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_multiprocessing_fork $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_multiprocessing_forkserver.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_multiprocessing_forkserver $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_multiprocessing_main_handling.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_multiprocessing_main_handling $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_multiprocessing_spawn.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_multiprocessing_spawn $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_netrc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_netrc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_nis.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_nis $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_nntplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_nntplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ntpath.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ntpath $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_numeric_tower.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_numeric_tower $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ossaudiodev.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ossaudiodev $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_parser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_parser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pathlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pathlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pdb.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pdb $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_peepholer.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_peepholer $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pipes.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pipes $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pkgimport.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pkgimport $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_platform.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_platform $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_plistlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_plistlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_httplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_httplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_popen.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_popen $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_poplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_poplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_posix.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_posix $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_posixpath.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_posixpath $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_profile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_profile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_property.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_property $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pstats.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pstats $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pty.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pty $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_py_compile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_py_compile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pyclbr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pyclbr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_pydoc.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_pydoc $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_readline.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_readline $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_regrtest.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_regrtest $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_repl.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_repl $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_resource.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_resource $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_richcmp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_richcmp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sched.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sched $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_script_helper.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_script_helper $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_shlex.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_shlex $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_shutil.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_shutil $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_signal.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_signal $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_site.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_site $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_smtpd.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_smtpd $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_smtplib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_smtplib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_smtpnet.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_smtpnet $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sndhdr.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sndhdr $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_socket.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_socket $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_socketserver.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_socketserver $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_spwd.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_spwd $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_startfile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_startfile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_statistics.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_statistics $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_string.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_string $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_string_literals.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_string_literals $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_strptime.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_strptime $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_structseq.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_structseq $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_subclassinit.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_subclassinit $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_subprocess.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_subprocess $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sunau.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sunau $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_support.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_support $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_symbol.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_symbol $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_symtable.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_symtable $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_sys_setprofile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_sys_setprofile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_syslog.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_syslog $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_telnetlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_telnetlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_threadedtempfile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_threadedtempfile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_timeit.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_timeit $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_timeout.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_timeout $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_tokenize.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_tokenize $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_trace.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_trace $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_traceback.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_traceback $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_turtle.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_turtle $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_unittest.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_unittest $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_univnewlines.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_univnewlines $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllib2.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllib2 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllib2_localnet.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllib2_localnet $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllib2net.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllib2net $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllib_response.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllib_response $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_urllibnet.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_urllibnet $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_wait3.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_wait3 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_wait4.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_wait4 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_webbrowser.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_webbrowser $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_xdrlib.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_xdrlib $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_weakref.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_weakref $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_weakset.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_weakset $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_zipapp.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_zipapp $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_zipimport.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_zipimport $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_zipfile.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_zipfile $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_zipfile64.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_zipfile64 $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/mp_preload.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.mp_preload $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/bisect.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.bisect $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/signalinterproctester.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.signalinterproctester $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/pythoninfo.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.pythoninfo $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/datetimetester.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.datetimetester $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/outstanding_bugs.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.outstanding_bugs $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/sortperf.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.sortperf $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_openpty.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_openpty $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_queue.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_queue $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_ordered_dict.py.runs: \ - o/$(MODE)/third_party/python/pythontester.com.dbg + o/$(MODE)/third_party/python/pythontester.com @$(COMPILE) -ACHECK -tT$@ $(PYHARNESSARGS) $< -m test.test_ordered_dict $(PYTESTARGS) ################################################################################ diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 72c17d6b4..8172f7d45 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -174,7 +174,7 @@ o/$(MODE)/third_party/quickjs/run-test262.com.dbg: \ $(THIRD_PARTY_QUICKJS_A).pkg \ o/$(MODE)/third_party/quickjs/run-test262.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/quickjs/unicode_gen.com.dbg: \ @@ -183,7 +183,7 @@ o/$(MODE)/third_party/quickjs/unicode_gen.com.dbg: \ $(THIRD_PARTY_QUICKJS_A).pkg \ o/$(MODE)/third_party/quickjs/unicode_gen.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(THIRD_PARTY_QUICKJS_OBJS): \ diff --git a/third_party/smallz4/smallz4.mk b/third_party/smallz4/smallz4.mk index aea621222..a47b7ca86 100644 --- a/third_party/smallz4/smallz4.mk +++ b/third_party/smallz4/smallz4.mk @@ -60,14 +60,14 @@ o/$(MODE)/third_party/smallz4/smallz4.com.dbg: \ $(THIRD_PARTY_SMALLZ4) \ o/$(MODE)/third_party/smallz4/smallz4.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/third_party/smallz4/smallz4cat.com.dbg: \ $(THIRD_PARTY_SMALLZ4) \ o/$(MODE)/third_party/smallz4/smallz4cat.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) THIRD_PARTY_SMALLZ4_COMS = \ diff --git a/tool/build/ar.c b/tool/build/ar.c index 2f62f31eb..c33da4e07 100644 --- a/tool/build/ar.c +++ b/tool/build/ar.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.internal.h" +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" @@ -26,12 +27,16 @@ #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" @@ -53,6 +58,8 @@ * This tool also adds a feature: it ignores directory parameters. This * is important because good Makefiles on Linux will generally have the * directory be a .a prerequisite so archives rebuild on file deletion. + * + * @see https://www.unix.com/man-page/opensolaris/3head/ar.h/ */ struct Args { @@ -80,29 +87,68 @@ struct Header { char fmag[2]; }; +static void *Realloc(void *p, size_t n) { + void *q; + if (!(q = realloc(p, n))) { + fputs("error: ar: out of memory\n", stderr); + exit(1); + } + return q; +} + +static void *Malloc(size_t n) { + return Realloc(0, n); +} + +static void NewArgs(struct Args *l, size_t n) { + l->i = 0; + l->n = MAX(2, n); + l->p = Malloc(l->n * sizeof(*l->p)); + l->p[0] = 0; +} + static void NewInts(struct Ints *l, size_t n) { l->i = 0; - l->n = n; - l->p = xmalloc(n * sizeof(int)); + l->n = MAX(2, n); + l->p = Malloc(l->n * sizeof(*l->p)); + l->p[0] = 0; } static void NewString(struct String *s, size_t n) { s->i = 0; - s->n = n; - s->p = xmalloc(n); + s->n = MAX(2, n); + s->p = Malloc(s->n * sizeof(*s->p)); + s->p[0] = 0; } static void AppendInt(struct Ints *l, int i) { - APPEND(&l->p, &l->i, &l->n, &i); + assert(l->n > 1); + if (l->i + 1 >= l->n) { + do { + l->n += l->n >> 1; + } while (l->i + 1 >= l->n); + l->p = Realloc(l->p, l->n * sizeof(*l->p)); + } + l->p[l->i++] = i; + l->p[l->i] = 0; } static void AppendArg(struct Args *l, char *s) { - APPEND(&l->p, &l->i, &l->n, &s); + assert(l->n > 1); + if (l->i + 1 >= l->n) { + do { + l->n += l->n >> 1; + } while (l->i + 1 >= l->n); + l->p = Realloc(l->p, l->n * sizeof(*l->p)); + } + l->p[l->i++] = s; + l->p[l->i] = 0; } static void MakeHeader(struct Header *h, const char *name, int ref, int mode, int size) { size_t n; + char ibuf[13], *p; memset(h, ' ', sizeof(*h)); n = strlen(name); memcpy(h->name, name, n); @@ -113,11 +159,15 @@ static void MakeHeader(struct Header *h, const char *name, int ref, int mode, h->date[0] = '0'; h->uid[0] = '0'; h->gid[0] = '0'; - FormatOctal32(h->mode, mode & 0777, false); + p = FormatOctal32(ibuf, mode & 0777, false); + CHECK_LE(p - ibuf, sizeof(h->mode)); + memcpy(h->mode, ibuf, p - ibuf); } h->fmag[0] = '`'; h->fmag[1] = '\n'; - FormatUint32(h->size, size); + p = FormatUint32(ibuf, size); + CHECK_LE(p - ibuf, sizeof(h->size)); + memcpy(h->size, ibuf, p - ibuf); } int main(int argc, char *argv[]) { @@ -151,15 +201,24 @@ int main(int argc, char *argv[]) { struct Header *header1, *header2; int i, j, fd, err, name, outfd, tablebufsize; - if (argc == 2 && !strcmp(argv[1], "-n")) exit(0); + // TODO(jart): Delete this. + if (argc == 2 && !strcmp(argv[1], "-n")) { + exit(0); + } + + // we only support one mode of operation, which is creating a new + // deterministic archive. this tool is so fast that we don't need + // database-like tools when editing static archives if (!(argc > 2 && strcmp(argv[1], "rcsD") == 0)) { - fprintf(stderr, "%s%s%s\n", "Usage: ", argv[0], " rcsD ARCHIVE FILE..."); - return 1; + fputs("usage: ", stderr); + if (argc > 0) fputs(argv[0], stderr); + fputs(" rcsD ARCHIVE FILE...", stderr); + exit(EX_USAGE); } outpath = argv[2]; - bzero(&args, sizeof(args)); - st = xmalloc(sizeof(struct stat)); + NewArgs(&args, 4); + st = Malloc(sizeof(struct stat)); NewInts(&modes, 128); NewInts(&names, 128); NewInts(&sizes, 128); @@ -206,10 +265,10 @@ int main(int argc, char *argv[]) { // compute length of output archive outsize = 0; tablebufsize = 4 + symnames.i * 4; - tablebuf = xmalloc(tablebufsize); - offsets = xmalloc(args.i * 4); - header1 = xmalloc(sizeof(struct Header)); - header2 = xmalloc(sizeof(struct Header)); + tablebuf = Malloc(tablebufsize); + offsets = Malloc(args.i * 4); + header1 = Malloc(sizeof(struct Header)); + header2 = Malloc(sizeof(struct Header)); iov[0].iov_base = "!\n"; outsize += (iov[0].iov_len = 8); iov[1].iov_base = header1; diff --git a/tool/build/build.mk b/tool/build/build.mk index 9df4dace4..e81c3f03b 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -56,9 +56,11 @@ TOOL_BUILD_DIRECTDEPS = \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ THIRD_PARTY_MBEDTLS \ + THIRD_PARTY_MUSL \ THIRD_PARTY_STB \ THIRD_PARTY_XED \ THIRD_PARTY_ZLIB \ + THIRD_PARTY_ZLIB_GZ \ TOOL_BUILD_LIB TOOL_BUILD_DEPS := \ @@ -78,7 +80,7 @@ o/$(MODE)/tool/build/%.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/%.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/blinkenlights.com.dbg: \ @@ -105,7 +107,7 @@ o/$(MODE)/tool/build/ar.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/ar.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/package.com.dbg: \ @@ -113,7 +115,7 @@ o/$(MODE)/tool/build/package.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/package.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/mkdeps.com.dbg: \ @@ -121,7 +123,7 @@ o/$(MODE)/tool/build/mkdeps.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/mkdeps.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/compile.com.dbg: \ @@ -129,7 +131,7 @@ o/$(MODE)/tool/build/compile.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/compile.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/zipobj.com.dbg: \ @@ -137,7 +139,7 @@ o/$(MODE)/tool/build/zipobj.com.dbg: \ o/$(MODE)/tool/build/build.pkg \ o/$(MODE)/tool/build/zipobj.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/build/emulator.o: \ diff --git a/tool/build/cocmd.c b/tool/build/cocmd.c new file mode 100644 index 000000000..c0e717803 --- /dev/null +++ b/tool/build/cocmd.c @@ -0,0 +1,220 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/fmt/itoa.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" + +/** + * @fileoverview Cosmopolitan Command Interpreter + * + * This is a lightweight command interpreter for GNU Make. It has just + * enough shell script language support to support our build config. + */ + +#define STATE_SHELL 0 +#define STATE_STR 1 + +char *p; +char *q; +char *cmd; +char *args[8192]; +const char *prog; +char argbuf[ARG_MAX]; +bool unsupported[256]; + +wontreturn void UnsupportedSyntax(unsigned char c) { + char ibuf[13]; + FormatOctal32(ibuf, c, true); + fputs(prog, stderr); + fputs(": unsupported shell syntax '", stderr); + fputc(c, stderr); + fputs("' (", stderr); + fputs(ibuf, stderr); + fputs("): ", stderr); + fputs(cmd, stderr); + fputs("\n", stderr); + exit(1); +} + +void Open(const char *path, int fd, int flags) { + const char *err; + close(fd); + if (open(path, flags, 0644) == -1) { + err = strerdoc(errno); + fputs(prog, stderr); + fputs(": failed to open '", stderr); + fputs(path, stderr); + fputs("': ", stderr); + fputs(err, stderr); + fputs("\n", stderr); + exit(1); + } +} + +char *Tokenize(void) { + int t; + char *r; + while (*p == ' ' || *p == '\t' || *p == '\n' || + (p[0] == '\\' && p[1] == '\n')) { + ++p; + } + if (!*p) return 0; + t = STATE_SHELL; + for (r = q;; ++p) { + switch (t) { + + case STATE_SHELL: + if (unsupported[*p & 255]) { + UnsupportedSyntax(*p); + } + if (!*p || *p == ' ' || *p == '\t') { + *q++ = 0; + return r; + } else if (*p == '\'') { + t = STATE_STR; + } else if (*p == '\\') { + if (!p[1]) UnsupportedSyntax(*p); + *q++ = *++p; + } else { + *q++ = *p; + } + break; + + case STATE_STR: + if (!*p) { + fputs("cmd: error: unterminated string\n", stderr); + exit(1); + } + if (*p == '\'') { + t = STATE_SHELL; + } else { + *q++ = *p; + } + break; + + default: + unreachable; + } + } +} + +int main(int argc, char *argv[]) { + char *s, *arg; + size_t i, j, n; + prog = argc > 0 ? argv[0] : "cocmd.com"; + + for (i = 1; i < 32; ++i) { + unsupported[i] = true; + } + unsupported['\t'] = false; + unsupported[0177] = true; + unsupported['~'] = true; + unsupported['`'] = true; + unsupported['#'] = true; + unsupported['$'] = true; + unsupported['*'] = true; + unsupported['('] = true; + unsupported[')'] = true; + unsupported['|'] = true; + unsupported['['] = true; + unsupported[']'] = true; + unsupported['{'] = true; + unsupported['}'] = true; + unsupported[';'] = true; + unsupported['"'] = true; + unsupported['?'] = true; + unsupported['!'] = true; + + if (argc != 3) { + fputs(prog, stderr); + fputs(": error: wrong number of args\n", stderr); + return 1; + } + + if (strcmp(argv[1], "-c")) { + fputs(prog, stderr); + fputs(": error: argv[1] should -c\n", stderr); + return 1; + } + + p = cmd = argv[2]; + if (strlen(cmd) >= ARG_MAX) { + fputs(prog, stderr); + fputs(": error: cmd too long: ", stderr); + fputs(cmd, stderr); + fputs("\n", stderr); + return 1; + } + + n = 0; + q = argbuf; + while ((arg = Tokenize())) { + if (n + 1 < ARRAYLEN(args)) { + if (!strcmp(arg, "2>&1")) { + close(1); + dup(2); + } else if (!strcmp(arg, ">&2")) { + close(2); + dup(1); + } else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') { + Open(arg + 3, 2, O_WRONLY | O_CREAT | O_APPEND); + } else if (arg[0] == '>' && arg[1] == '>') { + Open(arg + 2, 1, O_WRONLY | O_CREAT | O_APPEND); + } else if (arg[0] == '2' && arg[1] == '>') { + Open(arg + 2, 2, O_WRONLY | O_CREAT | O_TRUNC); + } else if (arg[0] == '>') { + Open(arg + 1, 1, O_WRONLY | O_CREAT | O_TRUNC); + } else if (arg[0] == '<') { + Open(arg + 1, 0, O_RDONLY); + } else { + args[n++] = arg; + } + } else { + fputs(prog, stderr); + fputs(": error: too many args\n", stderr); + return 1; + } + } + + if (!n) { + fputs(prog, stderr); + fputs(": error: too few args\n", stderr); + return 1; + } + + execv(args[0], args); + if (!n) { + s = strerdoc(errno); + fputs(prog, stderr); + fputs(": execve '", stderr); + fputs(args[0], stderr); + fputs("' failed: ", stderr); + fputs(s, stderr); + fputs("\n", stderr); + return 1; + } + + return 127; +} diff --git a/tool/build/compile.c b/tool/build/compile.c index 4088fc917..7705c048d 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -32,6 +32,7 @@ #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/log/color.internal.h" #include "libc/log/log.h" @@ -170,9 +171,7 @@ char *output; char *outpath; char *command; char *shortened; -char *cachedcmd; char *colorflag; -char *originalcmd; char ccpath[PATH_MAX]; struct stat st; @@ -582,7 +581,7 @@ int Launch(void) { timer.it_interval.tv_sec = timeout; setitimer(ITIMER_REAL, &timer, 0); } - pid = fork(); + pid = vfork(); #if 0 int fd; @@ -665,6 +664,8 @@ int Launch(void) { kill(pid, SIGKILL); gotalrm = 1; } + } else if (errno == ECHILD) { + break; } else { /* this should never happen */ PrintRed(); @@ -1040,44 +1041,6 @@ int main(int argc, char *argv[]) { } } - /* - * help error reporting code find symbol table - */ - if (startswith(cmd, "o/")) { - if (endswith(cmd, ".com")) { - s = xstrcat(cmd, ".dbg"); - } else { - s = xstrcat(cmd, ".com.dbg"); - } - if (fileexists(s)) { - AddEnv(xstrcat("COMDBG=", getcwd(0, 0), '/', s)); - } - } - - /* - * create assimilated atomic copies of ape binaries - */ - if (!IsWindows() && endswith(cmd, ".com")) { - if (!startswith(cmd, "o/")) { - cachedcmd = xstrcat("o/", cmd); - } else { - cachedcmd = xstrcat(xstripext(cmd), ".elf"); - } - if (FileExistsAndIsNewerThan(cachedcmd, cmd)) { - cmd = cachedcmd; - } else { - originalcmd = cmd; - FormatInt64(buf, getpid()); - cmd = xstrcat(originalcmd, ".tmp.", buf); - if (copyfile(originalcmd, cmd, COPYFILE_PRESERVE_TIMESTAMPS) == -1) { - fputs("error: compile.com failed to copy ape executable\n", stderr); - unlink(cmd); - exit(97); - } - } - args.p[0] = cmd; - } - /* * make sense of standard i/o file descriptors * we want to permit pipelines but prevent talking to terminal @@ -1117,26 +1080,6 @@ int main(int argc, char *argv[]) { * run command */ ws = Launch(); - if (ws != -1 && WIFEXITED(ws) && WEXITSTATUS(ws) == 127) { - if (startswith(cmd, "o/third_party/gcc") && - fileexists("third_party/gcc/unbundle.sh")) { - system("third_party/gcc/unbundle.sh"); - ws = Launch(); - } - } - - /* - * cleanup temporary copy of ape executable - */ - if (originalcmd) { - if (cachedcmd && WIFEXITED(ws) && !WEXITSTATUS(ws) && - IsNativeExecutable(cmd)) { - makedirs(xdirname(cachedcmd), 0755); - rename(cmd, cachedcmd); - } else { - unlink(cmd); - } - } /* * propagate exit diff --git a/tool/build/cp.c b/tool/build/cp.c new file mode 100644 index 000000000..f8c41dac7 --- /dev/null +++ b/tool/build/cp.c @@ -0,0 +1,234 @@ +/*-*- 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/copyfile.h" +#include "libc/calls/struct/stat.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/gc.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/ok.h" +#include "libc/x/x.h" +#include "third_party/getopt/getopt.h" +#include "third_party/musl/ftw.h" + +#define USAGE \ + " SRC... DST\n\ +\n\ +SYNOPSIS\n\ +\n\ + Copies Files\n\ +\n\ +FLAGS\n\ +\n\ + -?\n\ + -h help\n\ + -f force\n\ + -r recursive\n\ + -n no clobber\n\ + -a preserve all\n\ + -p preserve owner and timestamps\n\ +\n" + +int flags; +bool force; +int striplen; +bool recursive; +const char *prog; +char mkbuf[PATH_MAX]; +char srcdir[PATH_MAX]; +char dstdir[PATH_MAX]; +char srcfile[PATH_MAX]; +char dstfile[PATH_MAX]; +char linkbuf[PATH_MAX]; + +void Cp(char *, char *); + +bool IsDirectory(const char *path) { + int e; + bool res; + struct stat st; + e = errno; + res = stat(path, &st) != -1 && S_ISDIR(st.st_mode); + errno = e; + return res; +} + +bool IsSymlink(const char *path) { + int e; + bool res; + struct stat st; + e = errno; + res = fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1 && + S_ISLNK(st.st_mode); + errno = e; + return res; +} + +wontreturn void PrintUsage(int rc, FILE *f) { + fputs("usage: ", f); + fputs(prog, f); + fputs(USAGE, f); + exit(rc); +} + +void GetOpts(int argc, char *argv[]) { + int opt; + while ((opt = getopt(argc, argv, "?hfnaprR")) != -1) { + switch (opt) { + case 'f': + force = true; + break; + case 'r': + case 'R': + recursive = true; + break; + case 'n': + flags |= COPYFILE_NOCLOBBER; + break; + case 'a': + case 'p': + flags |= COPYFILE_PRESERVE_OWNER; + flags |= COPYFILE_PRESERVE_TIMESTAMPS; + break; + case 'h': + case '?': + PrintUsage(EXIT_SUCCESS, stdout); + default: + PrintUsage(EX_USAGE, stderr); + } + } +} + +int Visit(const char *fpath, const struct stat *sb, int tflag, + struct FTW *ftwbuf) { + char *src; + strcpy(srcfile, fpath); + src = srcfile + striplen; + strcpy(dstfile, dstdir); + if (!endswith(dstfile, "/")) { + strcat(dstfile, "/"); + } + strcat(dstfile, src); + strcpy(srcfile, fpath); + switch (tflag) { + case FTW_D: + return 0; + case FTW_F: + case FTW_SL: + case FTW_SLN: + Cp(srcfile, dstfile); + return 0; + default: + fputs(fpath, stderr); + fputs(": can't handle file type\n", stderr); + exit(1); + } +} + +char *Join(const char *a, const char *b) { + size_t n, m; + n = strlen(a); + m = strlen(b); + if (n + 1 + m + 1 > sizeof(dstfile)) { + fputs("error: cp: path too long\n", stderr); + exit(1); + } + stpcpy(stpcpy(stpcpy(dstfile, a), "/"), b); + return dstfile; +} + +void Cp(char *src, char *dst) { + ssize_t rc; + const char *s; + if (strlen(src) + 1 > PATH_MAX) _Exit(2); + if (strlen(dst) + 1 > PATH_MAX) _Exit(2); + basename(src); + basename(dst); + if (IsDirectory(src)) { + if (!recursive) { + fputs(prog, stderr); + fputs(": won't copy directory without -r flag.\n", stderr); + exit(1); + } + strcpy(dstdir, dst); + if (IsDirectory(dst)) { + strcpy(srcdir, src); + basename(srcdir); + striplen = 0; + strcpy(srcdir, basename(src)); + } else { + strcpy(srcdir, src); + basename(srcdir); + striplen = strlen(srcdir); + strcpy(srcdir, ""); + } + if (nftw(src, Visit, 20, 0) == -1) { + fputs(prog, stderr); + fputs(": nftw failed: ", stderr); + fputs(strerdoc(errno), stderr); + fputs("\n", stderr); + exit(1); + } + return; + } + if (IsDirectory(dst)) { + dst = Join(dst, basename(src)); + } + if (!force && access(dst, W_OK) == -1 && errno != ENOENT) goto OnFail; + strcpy(mkbuf, dst); + if (makedirs(dirname(mkbuf), 0755) == -1) goto OnFail; + if (IsSymlink(src)) { + if ((rc = readlink(src, linkbuf, sizeof(linkbuf) - 1)) == -1) goto OnFail; + linkbuf[rc] = 0; + if (symlink(linkbuf, dst) == -1) goto OnFail; + } else { + if (copyfile(src, dst, flags) == -1) goto OnFail; + } + return; +OnFail: + s = strerdoc(errno); + fputs(prog, stderr); + fputs(": ", stderr); + fputs(src, stderr); + fputs(" ", stderr); + fputs(dst, stderr); + fputs(": ", stderr); + fputs(s, stderr); + fputs("\n", stderr); + exit(1); +} + +int main(int argc, char *argv[]) { + int i; + prog = argc > 0 ? argv[0] : "cp.com"; + GetOpts(argc, argv); + if (argc - optind < 2) PrintUsage(EX_USAGE, stderr); + for (i = optind; i < argc - 1; ++i) { + Cp(argv[i], argv[argc - 1]); + } + return 0; +} diff --git a/tool/build/echo.c b/tool/build/echo.c new file mode 100644 index 000000000..4cc0fe8e8 --- /dev/null +++ b/tool/build/echo.c @@ -0,0 +1,46 @@ +/*-*- 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/stdio/stdio.h" +#include "libc/str/str.h" + +int main(int argc, char *argv[]) { + int i, j; + FILE *stream = stdout; + bool wantnewline = true; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-n")) { + wantnewline = false; + } else if (!strcmp(argv[i], "-2")) { + stream = stderr; + } else { + break; + } + } + + for (j = 0; i + j < argc; ++j) { + if (j) fputc(' ', stream); + fputs(argv[i + j], stream); + } + if (wantnewline) { + fputc('\n', stream); + } + + return 0; +} diff --git a/tool/build/gzip.c b/tool/build/gzip.c new file mode 100644 index 000000000..ce86f8217 --- /dev/null +++ b/tool/build/gzip.c @@ -0,0 +1,310 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/ok.h" +#include "third_party/getopt/getopt.h" +#include "third_party/zlib/zlib.h" + +#define USAGE \ + " PATH...\n\ +\n\ +SYNOPSIS\n\ +\n\ + Compress Files\n\ +\n\ +FLAGS\n\ +\n\ + -?\n\ + -h help\n\ + -f force\n\ + -c use stdout\n\ + -d decompress\n\ + -A append mode\n\ + -x exclusive mode\n\ + -k keep input file\n\ + -0 disable compression\n\ + -1 fastest compression\n\ + -4 coolest compression\n\ + -9 maximum compression\n\ + -a ascii mode (ignored)\n\ + -F fixed strategy (advanced)\n\ + -L filtered strategy (advanced)\n\ + -R run length strategy (advanced)\n\ + -H huffman only strategy (advanced)\n\ +\n" + +bool opt_keep; +bool opt_force; +char opt_level; +bool opt_append; +char opt_strategy; +bool opt_exclusive; +bool opt_usestdout; +bool opt_decompress; + +const char *prog; +char databuf[32768]; +char pathbuf[PATH_MAX]; + +wontreturn void PrintUsage(int rc, FILE *f) { + fputs("usage: ", f); + fputs(prog, f); + fputs(USAGE, f); + exit(rc); +} + +void GetOpts(int argc, char *argv[]) { + int opt; + while ((opt = getopt(argc, argv, "?hfcdakxALFRHF0123456789")) != -1) { + switch (opt) { + case 'k': + opt_keep = true; + break; + case 'f': + opt_force = true; + break; + case 'A': + opt_append = true; + break; + case 'c': + opt_usestdout = true; + break; + case 'x': + opt_exclusive = true; + break; + case 'd': + opt_decompress = true; + break; + case 'F': + opt_strategy = 'F'; // Z_FIXED + break; + case 'L': + opt_strategy = 'f'; // Z_FILTERED + break; + case 'R': + opt_strategy = 'R'; // Z_RLE + break; + case 'H': + opt_strategy = 'h'; // Z_HUFFMAN_ONLY + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + opt_level = opt; + break; + case 'h': + case '?': + PrintUsage(EXIT_SUCCESS, stdout); + default: + PrintUsage(EX_USAGE, stderr); + } + } +} + +void Compress(const char *inpath) { + FILE *input; + gzFile output; + int rc, n, errnum; + const char *outpath; + char *p, openflags[5]; + outpath = 0; + if (inpath) { + input = fopen(inpath, "rb"); + } else if (opt_usestdout && (opt_force || !isatty(1))) { + opt_usestdout = true; + inpath = "/dev/stdin"; + input = stdin; + } else { + fputs(prog, stderr); + fputs(": compressed data not written to a terminal." + " Use -f to force compression.\n", + stderr); + exit(1); + } + p = openflags; + *p++ = opt_append ? 'a' : 'w'; + *p++ = 'b'; + if (opt_exclusive) *p++ = 'x'; + if (opt_level) *p++ = opt_level; + if (opt_strategy) *p++ = opt_strategy; + *p = 0; + if (opt_usestdout) { + outpath = "/dev/stdout"; + output = gzdopen(0, openflags); + } else { + if (strlen(inpath) + 3 + 1 > PATH_MAX) _Exit(2); + stpcpy(stpcpy(pathbuf, inpath), ".gz"); + outpath = pathbuf; + output = gzopen(outpath, openflags); + } + if (!output) { + fputs(outpath, stderr); + fputs(": gzopen() failed\n", stderr); + fputs(strerdoc(errno), stderr); + fputs("\n", stderr); + exit(1); + } + do { + rc = fread(databuf, 1, sizeof(databuf), input); + if (rc == -1) { + errnum = 0; + fputs(inpath, stderr); + fputs(": read failed: ", stderr); + fputs(strerdoc(ferror(input)), stderr); + fputs("\n", stderr); + _Exit(1); + } + if (!gzwrite(output, databuf, rc)) { + fputs(outpath, stderr); + fputs(": gzwrite failed: ", stderr); + fputs(gzerror(output, &errnum), stderr); + fputs("\n", stderr); + _Exit(1); + } + } while (rc == sizeof(databuf)); + if (input != stdin) { + if (fclose(input)) { + fputs(inpath, stderr); + fputs(": close failed\n", stderr); + _Exit(1); + } + } + if (gzclose(output)) { + fputs(outpath, stderr); + fputs(": gzclose failed\n", stderr); + _Exit(1); + } + if (!opt_keep && !opt_usestdout && (opt_force || !access(inpath, W_OK))) { + unlink(inpath); + } +} + +void Decompress(const char *inpath) { + FILE *output; + gzFile input; + int rc, n, errnum; + const char *outpath; + outpath = 0; + if (inpath) { + input = gzopen(inpath, "rb"); + } else { + opt_usestdout = true; + inpath = "/dev/stdin"; + input = gzdopen(0, "rb"); + } + if (!input) { + fputs(inpath, stderr); + fputs(": gzopen() failed\n", stderr); + fputs(strerdoc(errno), stderr); + fputs("\n", stderr); + exit(1); + } + if (opt_usestdout) { + output = stdout; + outpath = "/dev/stdout"; + } else if (endswith(inpath, ".gz")) { + n = strlen(inpath); + if (n - 3 + 1 > PATH_MAX) _Exit(2); + memcpy(pathbuf, inpath, n - 3); + pathbuf[n - 3] = 0; + outpath = pathbuf; + if (!(output = fopen(outpath, opt_append ? "wa" : "wb"))) { + fputs(outpath, stderr); + fputs(": open failed: ", stderr); + fputs(strerdoc(errno), stderr); + fputs("\n", stderr); + _Exit(1); + } + } else { + fputs(inpath, stderr); + fputs(": needs to end with .gz unless -c is passed\n", stderr); + _Exit(1); + } + do { + rc = gzread(input, databuf, sizeof(databuf)); + if (rc == -1) { + errnum = 0; + fputs(inpath, stderr); + fputs(": gzread failed: ", stderr); + fputs(gzerror(input, &errnum), stderr); + fputs("\n", stderr); + _Exit(1); + } + if (fwrite(databuf, rc, 1, output) != 1) { + fputs(outpath, stderr); + fputs(": write failed: ", stderr); + fputs(strerdoc(ferror(output)), stderr); + fputs("\n", stderr); + _Exit(1); + } + } while (rc == sizeof(databuf)); + if (gzclose(input)) { + fputs(inpath, stderr); + fputs(": gzclose failed\n", stderr); + _Exit(1); + } + if (output != stdout) { + if (fclose(output)) { + fputs(outpath, stderr); + fputs(": close failed\n", stderr); + _Exit(1); + } + } + if (!opt_keep && !opt_usestdout && (opt_force || !access(inpath, W_OK))) { + unlink(inpath); + } +} + +int main(int argc, char *argv[]) { + int i; + prog = argc > 0 ? argv[0] : "cp.com"; + GetOpts(argc, argv); + if (opt_decompress) { + if (optind == argc) { + Decompress(0); + } else { + for (i = optind; i < argc; ++i) { + Decompress(argv[i]); + } + } + } else { + if (optind == argc) { + Compress(0); + } else { + for (i = optind; i < argc; ++i) { + Compress(argv[i]); + } + } + } + return 0; +} diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index ed828227f..a49b4cfc2 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -28,6 +28,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -225,7 +226,7 @@ wontreturn void OnMissingFile(const char *list, const char *src) { * automatically restart itself. */ if (list) { - fprintf(stderr, "%s %s...\n", "Refreshing", list); + kprintf("%s %s...\n", "Refreshing", list); unlink(list); } exit(1); @@ -249,7 +250,9 @@ void LoadRelationships(int argc, char *argv[]) { while ((src = getargs_next(&ga))) { if (ShouldSkipSource(src)) continue; srcid = GetSourceId(src, strlen(src)); - if ((fd = open(src, O_RDONLY)) == -1) OnMissingFile(ga.path, src); + if ((fd = open(src, O_RDONLY)) == -1) { + OnMissingFile(ga.path, src); + } CHECK_NE(-1, (rc = read(fd, buf, MAX_READ))); close(fd); size = rc; @@ -282,13 +285,13 @@ void GetOpts(int argc, char *argv[]) { buildroot = optarg; break; default: - fprintf(stderr, "%s: %s [-r %s] [-o %s] [%s...]\n", "Usage", argv[0], + kprintf("%s: %s [-r %s] [-o %s] [%s...]\n", "Usage", argv[0], "BUILDROOT", "OUTPUT", "PATHSFILE"); exit(1); } } - if (isempty(out)) fprintf(stderr, "need -o FILE"), exit(1); - if (isempty(buildroot)) fprintf(stderr, "need -r o/$(MODE)"), exit(1); + if (isempty(out)) kprintf("need -o FILE"), exit(1); + if (isempty(buildroot)) kprintf("need -r o/$(MODE)"), exit(1); } const char *StripExt(const char *s) { @@ -352,7 +355,7 @@ bool HasSameContent(void) { s = GetFileSizeOrZero(out); if (s == appendz(bout).i) { if (s) { - CHECK_NE(-1, (fd = open(out, O_RDONLY))); + CHECK_NE(-1, (fd = open(out, O_RDONLY)), "open(%#s)", out); CHECK_NE(MAP_FAILED, (m = mmap(0, s, PROT_READ, MAP_SHARED, fd, 0))); r = !bcmp(bout, m, s); munmap(m, s); @@ -394,7 +397,7 @@ int main(int argc, char *argv[]) { appendw(&bout, '\n'); } /* if (!fileexists(out) || !HasSameContent()) { */ - CHECK_NE(-1, (fd = open(out, O_CREAT | O_WRONLY, 0644))); + CHECK_NE(-1, (fd = open(out, O_CREAT | O_WRONLY, 0644)), "open(%#s)", out); CHECK_NE(-1, ftruncate(fd, appendz(bout).i)); CHECK_NE(-1, xwrite(fd, bout, appendz(bout).i)); CHECK_NE(-1, close(fd)); diff --git a/tool/build/mkdir.c b/tool/build/mkdir.c new file mode 100644 index 000000000..45b1d5bdc --- /dev/null +++ b/tool/build/mkdir.c @@ -0,0 +1,82 @@ +#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/errno.h" +#include "libc/fmt/conv.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/ex.h" +#include "libc/x/x.h" +#include "third_party/getopt/getopt.h" + +#define USAGE \ + " [-p] [-m MODE] DIR...\n\ +Utility for creating directories.\n\ +\n\ +FLAGS\n\ +\n\ + -h Help\n\ + -m MODE Octal mode\n\ + -p Make parent directories\n" + +const char *prog; + +wontreturn void PrintUsage(int rc, FILE *f) { + fputs("Usage: ", f); + fputs(prog, f); + fputs(USAGE, f); + exit(rc); +} + +int main(int argc, char *argv[]) { + int i, mode = 0755; + int (*mkdirp)(const char *, unsigned) = mkdir; + prog = argc > 0 ? argv[0] : "mkdir.com"; + + while ((i = getopt(argc, argv, "?hpm:")) != -1) { + switch (i) { + case 'p': + mkdirp = makedirs; + break; + case 'm': + mode = strtol(optarg, 0, 8); + break; + case '?': + case 'h': + PrintUsage(0, stdout); + default: + PrintUsage(EX_USAGE, stderr); + } + } + + if (optind == argc) { + fputs(prog, stderr); + fputs(": missing argument\n", stderr); + fputs("Try '", stderr); + fputs(prog, stderr); + fputs(" -h' for more information.\n", stderr); + exit(1); + } + + for (i = optind; i < argc; ++i) { + if (mkdirp(argv[i], mode) == -1) { + fputs(prog, stderr); + fputs(": cannot create directory '", stderr); + fputs(argv[i], stderr); + fputs("' ", stderr); + fputs(strerdoc(errno), stderr); + fputc('\n', stderr); + exit(1); + } + } + + return 0; +} diff --git a/tool/build/pwd.c b/tool/build/pwd.c new file mode 100644 index 000000000..2b7f59f01 --- /dev/null +++ b/tool/build/pwd.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 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/stdio/stdio.h" + +/** + * @fileoverview Tool for printing current directory. + */ + +char path[PATH_MAX]; + +int main(int argc, char *argv[]) { + char *p; + if ((p = getcwd(path, sizeof(path)))) { + fputs(p, stdout); + fputc('\n', stdout); + return 0; + } else { + return 1; + } +} diff --git a/tool/build/rm.c b/tool/build/rm.c new file mode 100644 index 000000000..a3d808f19 --- /dev/null +++ b/tool/build/rm.c @@ -0,0 +1,103 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/ok.h" +#include "third_party/getopt/getopt.h" + +#define USAGE \ + " FILE...\n\ +\n\ +SYNOPSIS\n\ +\n\ + Removes Files\n\ +\n\ +FLAGS\n\ +\n\ + -?\n\ + -h help\n\ + -f force\n\ +\n" + +bool force; +const char *prog; + +wontreturn void PrintUsage(int rc, FILE *f) { + fputs("usage: ", f); + fputs(prog, f); + fputs(USAGE, f); + exit(rc); +} + +void GetOpts(int argc, char *argv[]) { + int opt; + while ((opt = getopt(argc, argv, "?hf")) != -1) { + switch (opt) { + case 'f': + force = true; + break; + case 'h': + case '?': + PrintUsage(EXIT_SUCCESS, stdout); + default: + PrintUsage(EX_USAGE, stderr); + } + } +} + +void Remove(const char *path) { + const char *s; + if (!force && access(path, W_OK) == -1) goto OnFail; + if (unlink(path) == -1) goto OnFail; + return; +OnFail: + if (force && errno == ENOENT) return; + s = strerdoc(errno); + fputs(prog, stderr); + fputs(": cannot remove '", stderr); + fputs(path, stderr); + fputs("': ", stderr); + fputs(s, stderr); + fputs("\n", stderr); + exit(1); +} + +int main(int argc, char *argv[]) { + int i; + prog = argc > 0 ? argv[0] : "rm.com"; + + if (argc < 2) { + fputs(prog, stderr); + fputs(": missing operand\n" + "Try 'rm -h' for more information.\n", + stderr); + exit(1); + } + + GetOpts(argc, argv); + + for (i = optind; i < argc; ++i) { + Remove(argv[i]); + } +} diff --git a/tool/build/runitd.c b/tool/build/runitd.c index cf4d92128..0d0d54cee 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -305,6 +305,7 @@ void Recv(void *output, size_t outputsize) { // pass along eof condition to zlib INFOF("mbedtls_ssl_read"); received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf)); + if (!received) TlsDie("got unexpected eof", received); if (received < 0) TlsDie("read failed", received); // decompress packet completely // into a dynamical size buffer diff --git a/tool/build/touch.c b/tool/build/touch.c new file mode 100644 index 000000000..f9be6e8b7 --- /dev/null +++ b/tool/build/touch.c @@ -0,0 +1,46 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" + +/** + * @fileoverview Command for updating timestamps on files. + */ + +int main(int argc, char *argv[]) { + int i; + const char *s, *prog; + prog = argc > 0 ? argv[0] : "touch.com"; + for (i = 1; i < argc; ++i) { + if (touch(argv[i], 0666) == -1) { + s = strerdoc(errno); + fputs(prog, stderr); + fputs(": cannot touch '", stderr); + fputs(argv[i], stderr); + fputs("': ", stderr); + fputs(s, stderr); + fputs("\n", stderr); + exit(1); + } + } + return 0; +} diff --git a/tool/build/unbundle.c b/tool/build/unbundle.c new file mode 100644 index 000000000..7614af4ac --- /dev/null +++ b/tool/build/unbundle.c @@ -0,0 +1,89 @@ +/*-*- 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/dce.h" +#include "libc/errno.h" +#include "libc/fmt/itoa.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/x/x.h" +#include "third_party/musl/ftw.h" + +const char *prog; +char tmpdir[PATH_MAX]; +char binpath[PATH_MAX]; + +bool IsDirectory(const char *path) { + int e; + bool res; + struct stat st; + e = errno; + res = stat(path, &st) != -1 && S_ISDIR(st.st_mode); + errno = e; + return res; +} + +void Execute(char *argv[]) { + int ws; + if (!vfork()) { + execv(argv[0], argv); + _Exit(127); + } + wait(&ws); + if (!WIFEXITED(ws) || WEXITSTATUS(ws)) { + fputs(argv[0], stderr); + fputs(": command failed\n", stderr); + exit(1); + } +} + +int Visit(const char *fpath, const struct stat *sb, int tflag, + struct FTW *ftwbuf) { + if (tflag == FTW_F && endswith(fpath, ".gz")) { + Execute((char *[]){"build/bootstrap/gzip.com", "-d", fpath, 0}); + strcpy(binpath, fpath); + binpath[strlen(binpath) - 3] = 0; + chmod(binpath, 0755); + } else if (tflag == FTW_F && endswith(fpath, ".sym")) { + strcpy(binpath, fpath); + binpath[strlen(binpath) - 4] = 0; + symlink(xslurp(fpath, 0), binpath); + } + return 0; +} + +int main(int argc, char *argv[]) { + if (!IsLinux()) return 0; + prog = argc > 0 ? argv[0] : "unbundle.com"; + if (IsDirectory("o/third_party/gcc")) return 0; + makedirs("o/third_party", 0755); + FormatInt32(stpcpy(tmpdir, "o/third_party/gcc."), getpid()); + Execute( + (char *[]){"build/bootstrap/cp.com", "-r", "third_party/gcc", tmpdir, 0}); + if (nftw(tmpdir, Visit, 20, 0) == -1) { + fputs(prog, stderr); + fputs(": nftw failed: ", stderr); + fputs(strerdoc(errno), stderr); + fputs("\n", stderr); + exit(1); + } + rename(tmpdir, "o/third_party/gcc"); +} diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index bdaa29025..1d92c2c1f 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -20,6 +20,7 @@ #include "libc/calls/struct/stat.h" #include "libc/elf/def.h" #include "libc/fmt/conv.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -51,8 +52,8 @@ const char *path_prefix_; struct timespec timestamp; size_t kZipCdirHdrLinkableSizeBootstrap; -wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "%s%s%s\n", "Usage: ", program_invocation_name, +wontreturn void PrintUsage(int rc) { + kprintf("%s%s%s\n", "Usage: ", program_invocation_name, " [-n] [-B] [-C INT] [-P PREFIX] [-o FILE] [-s SYMBOL] [-y YOINK] " "[FILE...]"); exit(rc); @@ -99,9 +100,9 @@ void GetOpts(int *argc, char ***argv) { break; case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(EXIT_SUCCESS); default: - PrintUsage(EX_USAGE, stderr); + PrintUsage(EX_USAGE); } } *argc -= optind; @@ -137,7 +138,7 @@ void ProcessFile(struct ElfWriter *elf, const char *path) { if (S_ISDIR(st.st_mode)) { st.st_size = 0; if (!endswith(name, "/")) { - name = gc(xasprintf("%s/", name)); + name = gc(xstrcat(name, '/')); } } elfwriter_zip(elf, name, name, strlen(name), map, st.st_size, st.st_mode, diff --git a/tool/decode/ar.c b/tool/decode/ar.c index f27f3b983..1066ef70a 100644 --- a/tool/decode/ar.c +++ b/tool/decode/ar.c @@ -20,6 +20,7 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/fmt/conv.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/mem/mem.h" @@ -50,7 +51,7 @@ static void PrintString(uint8_t *p, long n, const char *name) { s = xmalloc(n + 1); s[n] = '\0'; memcpy(s, p, n); - printf("\t.ascii\t%`'.*s\t\t\t# %s\n", n, s, name); + printf("\t.ascii\t%-`'*.*s# %s\n", 35, n, s, name); free(s); } @@ -94,8 +95,8 @@ static void PrintHeader(uint8_t *p) { static void Print(void) { int arsize; - uint8_t *b, *p; uint64_t offset; + uint8_t *b, *p, *e; uint32_t i, n, o, table, entries, symbols, symbolslen; arsize = atoi((char *)(data + 8 + 48)); CHECK_LE(4, arsize); @@ -107,18 +108,19 @@ static void Print(void) { PrintHeader(data + 8); printf("\n"); - printf("\t.long\t%u\t\t\t# %s\n", entries, "symbol table entries"); + printf("\t.long\t%-*.u# %s\n", 35, entries, "symbol table entries"); table = 8 + 60 + 4; for (i = 0; i < entries; ++i) { - printf("\t.long\t%#x\t\t\t\t# %u\n", READ32BE(data + table + i * 4), i); + printf("\t.long\t%#-*.x# %u\n", 35, READ32BE(data + table + i * 4), i); } symbols = table + entries * 4; - symbolslen = arsize - (4 + entries * 4); + symbolslen = arsize - (entries + 1) * 4; for (i = o = 0; o < symbolslen; ++i, o += n + 1) { b = data + symbols + o; - CHECK_NOTNULL((p = memchr(b, '\0', symbolslen - (symbols + o)))); + CHECK_NOTNULL((p = memchr(b, '\0', symbolslen - o)), "%p %p %p %p %`.s", b, + data, symbols, o, b); n = p - b; - printf("\t.asciz\t%#`'.*s\t\t\t# %u\n", n, b, i); + printf("\t.asciz\t%#-`'*.*s# %u\n", 35, n, b, i); } offset = 8 + 60 + arsize; diff --git a/tool/decode/decode.mk b/tool/decode/decode.mk index 6ecf4d2e0..997b22a79 100644 --- a/tool/decode/decode.mk +++ b/tool/decode/decode.mk @@ -56,7 +56,7 @@ o/$(MODE)/tool/decode/%.com.dbg: \ o/$(MODE)/tool/decode/%.o \ o/$(MODE)/tool/decode/decode.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TOOL_DECODE_OBJS): \ diff --git a/tool/decode/zip.c b/tool/decode/zip.c index 69acdc2c6..c735143b3 100644 --- a/tool/decode/zip.c +++ b/tool/decode/zip.c @@ -57,7 +57,6 @@ dontdiscard char *FormatDosTime(uint16_t dostime) { } void AdvancePosition(uint8_t *map, size_t *pos, size_t off) { - CHECK_GE(off, *pos); if (off > *pos) { printf("\n/\t<%s>\n", "LIMBO"); disassemblehex(&map[*pos], off - *pos, stdout); diff --git a/tool/hash/hash.mk b/tool/hash/hash.mk index 2e7c4628b..456c76c9f 100644 --- a/tool/hash/hash.mk +++ b/tool/hash/hash.mk @@ -37,7 +37,7 @@ o/$(MODE)/tool/hash/%.com.dbg: \ o/$(MODE)/tool/hash/%.o \ o/$(MODE)/tool/hash/hash.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) $(TOOL_HASH_OBJS): \ diff --git a/tool/lambda/lambda.mk b/tool/lambda/lambda.mk index c8cfdcc49..c7ede2705 100644 --- a/tool/lambda/lambda.mk +++ b/tool/lambda/lambda.mk @@ -44,7 +44,7 @@ o/$(MODE)/tool/lambda/%.com.dbg: \ o/$(MODE)/tool/lambda/%.o \ o/$(MODE)/tool/lambda/lambda.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/lambda/tromp.o: \ diff --git a/tool/net/help.txt b/tool/net/help.txt index 5a2630317..dd5430e40 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -225,10 +225,16 @@ USAGE Your redbean is an actually portable executable, that's able to run on six different operating systems. To do that, it needs to - overwrite its own MZ header at startup, with ELF or Mach-O, and - then puts the original back once the program loads. If you want - your redbean to follow the platform-local executable convention - then delete the /.ape file from zip. + extract a 4kb loader program to ${TMPDIR:-/tmp}/ape that'll map + your redbean into memory. It does however check to see if `ape` + is on the system path beforehand. You can also "assimilate" any + redbean into the platform-local executable format by running: + + $ file redbean.com + redbean.com: DOS/MBR boot sector + $ ./redbean.com --assimilate + $ file redbean.com + redbean.com: ELF 64-bit LSB executable redbean contains software licensed ISC, MIT, BSD-2, BSD-3, zlib which makes it a permissively licensed gift to anyone who might @@ -331,18 +337,22 @@ REPL #!/usr/bin/redbean -i print('hello world') - However operating systems like Linux usually require that script - interperters be in the local executable format. You can "assimilate" - and install your redbean using the following commands: + However UNIX operating systems usually require that interperters be + encoded in its preferred executable format. You can assimilate your + redbean into the local format using the following commands: - zip -d redbean.com .ape # remove the ape header - ./redbean.com -h >/dev/null # assimilate the binary - sudo cp redbean.com /usr/bin/redbean + $ file redbean.com + redbean.com: DOS/MBR boot sector + $ ./redbean.com --assimilate + $ file redbean.com + redbean.com: ELF 64-bit LSB executable + $ sudo cp redbean.com /usr/bin/redbean By following the above steps, redbean can be installed systemwide for multiple user accounts. It's also possible to chmod the binary to have - setuid privileges, provided it's configured to drop privileges in the - most appropriate manner; see the UNIX section for further details. + setuid privileges. Please note that, if you do this, the UNIX section + provides further details on APIs like `unix.setuid` that will help you + remove root privileges from the process in the appropriate manner. ──────────────────────────────────────────────────────────────────────────────── diff --git a/tool/net/net.mk b/tool/net/net.mk index 6332a72b1..15b075934 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -21,7 +21,6 @@ TOOL_NET_COMS = \ 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/redbean-assimilate.com \ o/$(MODE)/tool/net/wb.com TOOL_NET_CHECKS = \ @@ -82,7 +81,7 @@ o/$(MODE)/tool/net/%.com.dbg: \ o/$(MODE)/tool/net/%.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) # REDBEAN.COM @@ -100,7 +99,7 @@ o/$(MODE)/tool/net/redbean.com.dbg: \ o/$(MODE)/tool/net/largon2.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) ifneq ($(MODE),tiny) @@ -115,13 +114,11 @@ o/$(MODE)/tool/net/redbean.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/net/.redbean @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/net/.redbean/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean/.ape \ tool/net/help.txt \ tool/net/.init.lua \ tool/net/favicon.ico \ @@ -138,10 +135,8 @@ o/tiny/tool/net/redbean.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/tiny/tool/net/.redbean - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/tiny/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/tiny/tool/net/.redbean @$(COMPILE) -AZIP -T$@ o/tiny/third_party/zip/zip.com -9qj $@ \ - o/tiny/tool/net/.redbean/.ape \ tool/net/tiny/help.txt \ tool/net/.init.lua \ tool/net/favicon.ico \ @@ -156,10 +151,8 @@ o/tinylinux/tool/net/redbean.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/tinylinux/tool/net/.redbean - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/tinylinux/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/tinylinux/tool/net/.redbean @$(COMPILE) -AZIP -T$@ o/tinylinux/third_party/zip/zip.com -9qj $@ \ - o/tinylinux/tool/net/.redbean/.ape \ tool/net/tiny/help.txt \ tool/net/.init.lua \ tool/net/favicon.ico \ @@ -255,7 +248,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/demo/.reload.lua.zip.o \ o/$(MODE)/tool/net/demo/.init.lua.zip.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/net/redbean-demo.com: \ @@ -264,14 +257,12 @@ o/$(MODE)/tool/net/redbean-demo.com: \ o/$(MODE)/third_party/zip/zip.com \ tool/net/help.txt @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-demo - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/net/.redbean-demo @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-demo/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/net/.redbean-demo/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean-demo/.ape \ tool/net/help.txt # REDBEAN-STATIC.COM @@ -287,14 +278,12 @@ o/$(MODE)/tool/net/redbean-static.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-static - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/net/.redbean-static @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-static/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/net/.redbean-static/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean-static/.ape \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -304,7 +293,7 @@ o/$(MODE)/tool/net/redbean-static.com.dbg: \ o/$(MODE)/tool/net/redbean-static.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/net/redbean-static.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @@ -324,14 +313,12 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-unsecure - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-unsecure/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/net/.redbean-unsecure @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-unsecure/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/net/.redbean-unsecure/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean-unsecure/.ape \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -346,7 +333,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/net/redbean-unsecure.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @@ -368,14 +355,12 @@ o/$(MODE)/tool/net/redbean-original.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-original - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/net/.redbean-original @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-original/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/net/.redbean-original/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean-original/.ape \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -389,10 +374,8 @@ o/tiny/tool/net/redbean-original.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/tiny/tool/net/.redbean-original - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/tiny/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/tiny/tool/net/.redbean-original @$(COMPILE) -AZIP -T$@ o/tiny/third_party/zip/zip.com -9qj $@ \ - o/tiny/tool/net/.redbean-original/.ape \ tool/net/tiny/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -404,10 +387,8 @@ o/tinylinux/tool/net/redbean-original.com: \ tool/net/favicon.ico \ tool/net/redbean.png @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/tinylinux/tool/net/.redbean-original - @$(COMPILE) -ADD -T$@ dd if=$@ of=o/tinylinux/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/tinylinux/tool/net/.redbean-original @$(COMPILE) -AZIP -T$@ o/tinylinux/third_party/zip/zip.com -9qj $@ \ - o/tinylinux/tool/net/.redbean-original/.ape \ tool/net/tiny/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -417,7 +398,7 @@ o/$(MODE)/tool/net/redbean-original.com.dbg: \ o/$(MODE)/tool/net/redbean-original.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/net/redbean-original.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @@ -426,35 +407,6 @@ o/$(MODE)/tool/net/redbean-original.o: tool/net/redbean.c o/$(MODE)/tool/net/red o/$(MODE)/tool/net/redbean-original.s: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @$(COMPILE) -AOBJECTIFY.c $(COMPILE.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $< -# REDBEAN-ASSIMILATE.COM -# -# Same as REDBEAN.COM except without no-modify-self behavior. - -o/$(MODE)/tool/net/redbean-assimilate.com.dbg: \ - o/$(MODE)/tool/net/redbean.com.dbg - @cp -f $< $@ - -o/$(MODE)/tool/net/redbean-assimilate.com: \ - o/$(MODE)/tool/net/redbean-assimilate.com.dbg \ - o/$(MODE)/third_party/zip/zip.com \ - o/$(MODE)/tool/build/symtab.com \ - tool/net/net.mk \ - tool/net/help.txt \ - tool/net/.init.lua \ - tool/net/favicon.ico \ - tool/net/redbean.png - @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate - @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ - o/$(MODE)/tool/net/.redbean-assimilate/.symtab - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ - o/$(MODE)/tool/net/.redbean-assimilate/.symtab \ - tool/net/help.txt \ - tool/net/.init.lua \ - tool/net/favicon.ico \ - tool/net/redbean.png - .PHONY: o/$(MODE)/tool/net o/$(MODE)/tool/net: \ $(TOOL_NET_BINS) \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 03967ce58..105130002 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -6735,31 +6735,24 @@ static int HandleConnection(size_t i) { return rc; } -static void RestoreApe(void) { +static void MakeExecutableModifiable(void) { int ft; - char *p; size_t n; - struct Asset *a; extern char ape_rom_vaddr[] __attribute__((__weak__)); if (!(SUPPORT_VECTOR & (METAL | WINDOWS | XNU))) return; if (IsWindows()) return; // TODO if (IsOpenbsd()) return; // TODO if (IsNetbsd()) return; // TODO if (endswith(zpath, ".com.dbg")) return; - if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { - close(zfd); - ft = __ftrace; - if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) { - WARNF("(srvr) can't restore .ape"); - } - if (ft > 0) { - __ftrace = 0; - ftrace_install(); - __ftrace = ft; - } - free(p); - } else { - DEBUGF("(srvr) /.ape not found"); + close(zfd); + ft = __ftrace; + if ((zfd = OpenExecutable()) == -1) { + WARNF("(srvr) can't restore .ape"); + } + if (ft > 0) { + __ftrace = 0; + ftrace_install(); + __ftrace = ft; } } @@ -7209,7 +7202,7 @@ void RedBean(int argc, char *argv[]) { CHECK_NE(-1, (zfd = open(zpath, O_RDONLY))); CHECK_NE(-1, fstat(zfd, &zst)); OpenZip(true); - RestoreApe(); + MakeExecutableModifiable(); SetDefaults(); LuaStart(); GetOpts(argc, argv); diff --git a/tool/plinko/plinko.mk b/tool/plinko/plinko.mk index f841c1c40..21684cb10 100644 --- a/tool/plinko/plinko.mk +++ b/tool/plinko/plinko.mk @@ -43,7 +43,7 @@ o/$(MODE)/tool/plinko/%.com.dbg: \ o/$(MODE)/tool/plinko/plinko.pkg \ o/$(MODE)/tool/plinko/lib/library.lisp.zip.o \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) .PRECIOUS: o/$(MODE)/tool/plinko/plinko.com @@ -53,7 +53,7 @@ o/$(MODE)/tool/plinko/plinko.com: \ o/$(MODE)/tool/build/symtab.com \ tool/plinko/plinko.mk @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ - @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/plinko/.redbean + @$(COMPILE) -AMKDIR -T$@ $(MKDIR) o/$(MODE)/tool/plinko/.redbean @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/plinko/.plinko/.symtab $< @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/plinko/.plinko/.symtab diff --git a/tool/viz/viz.mk b/tool/viz/viz.mk index 744f644f8..17861744c 100644 --- a/tool/viz/viz.mk +++ b/tool/viz/viz.mk @@ -69,7 +69,7 @@ o/$(MODE)/tool/viz/%.com.dbg: \ o/$(MODE)/tool/viz/%.o \ o/$(MODE)/tool/viz/viz.pkg \ $(CRT) \ - $(APE) + $(APE_NO_MODIFY_SELF) @$(APELINK) o/$(MODE)/tool/viz/printimage.com.dbg: \ From 1a29424c52fdfcac6106410778518df430413274 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 25 May 2022 14:06:11 -0700 Subject: [PATCH 38/40] Fix ARG_MAX with chibicc documentation generator --- Makefile | 3 ++- third_party/chibicc/chibicc.main.c | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f5e15c7a2..7e6952bab 100644 --- a/Makefile +++ b/Makefile @@ -354,9 +354,10 @@ o/cosmopolitan.h: \ o/cosmopolitan.html: \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg \ $(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) + $(file >$@.args,$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))) o/$(MODE)/third_party/chibicc/chibicc.com.dbg -J \ -fno-common -include libc/integral/normalize.inc -o $@ \ - $(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) + @$@.args $(SRCS): \ libc/integral/normalize.inc \ diff --git a/third_party/chibicc/chibicc.main.c b/third_party/chibicc/chibicc.main.c index 6c218fcb6..537a9a4c7 100644 --- a/third_party/chibicc/chibicc.main.c +++ b/third_party/chibicc/chibicc.main.c @@ -1,7 +1,22 @@ #include "libc/mem/arena.h" #include "libc/runtime/internal.h" +#include "libc/x/x.h" #include "third_party/chibicc/chibicc.h" +#include "tool/build/lib/getargs.h" int main(int argc, char **argv) { - return chibicc(argc, argv); + int n; + char **p; + const char *arg; + struct GetArgs ga; + n = 0; + p = xcalloc(n + 1, sizeof(*p)); + getargs_init(&ga, argv); + while ((arg = getargs_next(&ga))) { + p = xrealloc(p, (++n + 1) * sizeof(*p)); + p[n - 1] = xstrdup(arg); + p[n - 0] = 0; + } + getargs_destroy(&ga); + return chibicc(n, p); } From 8f12cd980df6c8767158967df994bb7ae5639a8e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 25 May 2022 17:19:46 -0700 Subject: [PATCH 39/40] Fix execution bug w/o binfmt_misc The longjmp relocation in the master boot record coincidentally had a quote character in it, which caused the master boot record to be used when interpreter by the shell. The solution, is to move the grub stub below the long mode loader so the real mode loader relocation is near the master boot record. This change includes a regression test. --- ape/ape.S | 83 +++++++++++++++++----------------- test/libc/runtime/ape_test.c | 14 ++++++ third_party/chibicc/chibicc.mk | 10 ++-- 3 files changed, 61 insertions(+), 46 deletions(-) diff --git a/ape/ape.S b/ape/ape.S index 5074682c0..ab1d19cd8 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -553,6 +553,7 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .ascii "[ x\"$1\" != x--assimilate ] && {\n" .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" .ascii "[ -x \"$t\" ] || {\n" + .ascii "mkdir -p \"${t%/*}\" &&\n" .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" .shstub ape_loader_dd_skip,2 .ascii "\" count=\"" @@ -1168,47 +1169,6 @@ gdt: .short 2f-1f # table byte length .quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48 2: .endobj gdt,global,hidden -/*─────────────────────────────────────────────────────────────────────────────╗ -│ αcτµαlly pδrταblε εxεcµταblε § multiboot stub │ -╚──────────────────────────────────────────────────────────────────────────────╝ - boot modernized for the nineties */ - -#define GRUB_MAGIC 0x1BADB002 -#define GRUB_EAX 0x2BADB002 -#define GRUB_AOUT (1 << 16) -#define GRUB_CHECKSUM(FLAGS) (-(GRUB_MAGIC + (FLAGS)) & 0xffffffff) - -// Grub Header. - .align 4 -ape_grub: - .long GRUB_MAGIC # Magic - .long GRUB_AOUT # Flags - .long GRUB_CHECKSUM(GRUB_AOUT) # Checksum - .long RVA(ape_grub) # HeaderPhysicalAddress - .long IMAGE_BASE_PHYSICAL # TextPhysicalAddress - .long PHYSICAL(_edata) # LoadEndPhysicalAddress - .long PHYSICAL(_end) # BssEndPhysicalAddress - .long RVA(ape_grub_entry) # EntryPhysicalAddress - .endobj ape_grub,globl - -// Grub Entrypoint. -// Takes CPU out of legacy mode and jumps to normal entrypoint. -// @noreturn - .align 4 -ape_grub_entry: - .code32 -// cmp $GRUB_EAX,%eax -// jne triplf - push $0 - popf - mov $0x40,%dl - mov %cr0,%eax - and $~CR0_PE,%eax - mov %eax,%cr0 - ljmpw $0,$REAL(pc) - .code16 - .endfn ape_grub_entry - /*─────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § real mode │ ╚──────────────────────────────────────────────────────────────────────────────╝ @@ -1485,6 +1445,47 @@ long: push $GDT_LONG_DATA jmp *%rax .endfn long +/*─────────────────────────────────────────────────────────────────────────────╗ +│ αcτµαlly pδrταblε εxεcµταblε § multiboot stub │ +╚──────────────────────────────────────────────────────────────────────────────╝ + boot modernized for the nineties */ + +#define GRUB_MAGIC 0x1BADB002 +#define GRUB_EAX 0x2BADB002 +#define GRUB_AOUT (1 << 16) +#define GRUB_CHECKSUM(FLAGS) (-(GRUB_MAGIC + (FLAGS)) & 0xffffffff) + +// Grub Header. + .align 4 +ape_grub: + .long GRUB_MAGIC # Magic + .long GRUB_AOUT # Flags + .long GRUB_CHECKSUM(GRUB_AOUT) # Checksum + .long RVA(ape_grub) # HeaderPhysicalAddress + .long IMAGE_BASE_PHYSICAL # TextPhysicalAddress + .long PHYSICAL(_edata) # LoadEndPhysicalAddress + .long PHYSICAL(_end) # BssEndPhysicalAddress + .long RVA(ape_grub_entry) # EntryPhysicalAddress + .endobj ape_grub,globl + +// Grub Entrypoint. +// Takes CPU out of legacy mode and jumps to normal entrypoint. +// @noreturn + .align 4 +ape_grub_entry: + .code32 +// cmp $GRUB_EAX,%eax +// jne triplf + push $0 + popf + mov $0x40,%dl + mov %cr0,%eax + and $~CR0_PE,%eax + mov %eax,%cr0 + ljmpw $0,$REAL(pc) + .code64 + .endfn ape_grub_entry + /* ▄▄▒▀▀▀▀▒▒▄ █████▓▓▄░░░░ ▒▒▄ ▐█▓▓█▓▄█████▓░ ▀▒▄ diff --git a/test/libc/runtime/ape_test.c b/test/libc/runtime/ape_test.c index 6a765435a..bfee9dd8c 100644 --- a/test/libc/runtime/ape_test.c +++ b/test/libc/runtime/ape_test.c @@ -105,6 +105,20 @@ void RunApeTest(const char *path) { EXPECT_STREQN("MZqFpD", buf, 6); } +TEST(ape, noAccidentalQuotesInMasterBootRecord) { + int i, quotes = 0; + char buf[512] = {0}; + EXPECT_SYS(0, 3, open("bin/apetest.com", O_RDONLY)); + EXPECT_SYS(0, 512, read(3, buf, 512)); + EXPECT_SYS(0, 0, close(3)); + for (i = 0; i < 512; ++i) { + if (buf[i] == '\'') { + ++quotes; + } + } + EXPECT_EQ(1, quotes); +} + TEST(apeNoModifySelf, runsWithoutModifyingSelf) { RunApeTest("bin/apetest.com"); } diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index 892379df5..4f2e9d449 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -10,8 +10,8 @@ # This makefile compiles and runs each test twice. The first with # GCC-built chibicc, and a second time with chibicc-built chibicc -CHIBICC = o/$(MODE)/third_party/chibicc/chibicc.com.dbg -CHIBICC2 = o/$(MODE)/third_party/chibicc/chibicc2.com.dbg +CHIBICC = o/$(MODE)/third_party/chibicc/chibicc.com +CHIBICC2 = o/$(MODE)/third_party/chibicc/chibicc2.com CHIBICC_FLAGS = \ -fno-common \ -include libc/integral/normalize.inc \ @@ -134,11 +134,11 @@ o/$(MODE)/third_party/chibicc/chibicc.o: \ o/$(MODE)/third_party/chibicc/chibicc.chibicc.o: \ CHIBICC_FLAGS += $(THIRD_PARTY_CHIBICC_DEFINES) -o/$(MODE)/%.chibicc.o: %.s o/$(MODE)/third_party/chibicc/chibicc.com.dbg +o/$(MODE)/%.chibicc.o: %.s $(CHIBICC) @$(COMPILE) -ACHIBICC -T$@ $(CHIBICC) $(CHIBICC_FLAGS) -c -o $@ $< -o/$(MODE)/%.chibicc.o: %.c o/$(MODE)/third_party/chibicc/chibicc.com.dbg +o/$(MODE)/%.chibicc.o: %.c $(CHIBICC) @$(COMPILE) -ACHIBICC -T$@ $(CHIBICC) $(CHIBICC_FLAGS) -c -o $@ $< -o/$(MODE)/%.chibicc2.o: %.c o/$(MODE)/third_party/chibicc/chibicc2.com.dbg +o/$(MODE)/%.chibicc2.o: %.c $(CHIBICC2) @$(COMPILE) -ACHIBICC2 -T$@ $(CHIBICC2) $(CHIBICC_FLAGS) -c -o $@ $< THIRD_PARTY_CHIBICC_LIBS = $(foreach x,$(THIRD_PARTY_CHIBICC_ARTIFACTS),$($(x))) From 10b97ca630deb141fdc3b63943c70505047e438f Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 25 May 2022 22:29:10 -0700 Subject: [PATCH 40/40] Add test for sendfile() and reduce branches --- libc/calls/strace.internal.h | 2 +- libc/intrin/tls.greg.c | 2 - libc/sock/sendfile.c | 70 ++++++++++++++++++++++++++------- test/libc/sock/sendfile_test.c | 71 ++++++++++++++++++++++++++++++++++ third_party/mbedtls/ecp256.c | 30 ++++++++------ third_party/mbedtls/ecp384.c | 33 +++++++++------- 6 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 test/libc/sock/sendfile_test.c diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 70d158183..5cd8e5c91 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -11,7 +11,7 @@ #define _DATATRACE 1 /* not configurable w/ flag yet */ #define _NTTRACE 0 /* not configurable w/ flag yet */ -#define STRACE_PROLOGUE "%rSYS %5P %'18T " +#define STRACE_PROLOGUE "%rSYS %6P %'18T " #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c index e3250d190..3dc87cab7 100644 --- a/libc/intrin/tls.greg.c +++ b/libc/intrin/tls.greg.c @@ -61,8 +61,6 @@ privileged void *__initialize_tls(char tib[64]) { */ privileged void __install_tls(char tib[64]) { int ax, dx; - uint64_t magic; - unsigned char *p; assert(tib); assert(!__tls_enabled); assert(*(int *)(tib + 0x38) != -1); diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 01291b628..11d0d6b13 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -19,35 +19,78 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/nt/enum/wait.h" +#include "libc/nt/errors.h" +#include "libc/nt/files.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sendfile.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +// sendfile() isn't specified as raising eintr +static textwindows int SendfileBlock(int64_t handle, + struct NtOverlapped *overlapped) { + uint32_t i, got, flags = 0; + if (WSAGetLastError() != kNtErrorIoPending) { + NTTRACE("TransmitFile failed %lm"); + return __winsockerr(); + } + for (;;) { + i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true, + __SIG_POLLING_INTERVAL_MS, true); + if (i == kNtWaitFailed) { + NTTRACE("WSAWaitForMultipleEvents failed %lm"); + return __winsockerr(); + } else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) { + _check_interrupts(true, g_fds.p); +#if _NTTRACE + POLLTRACE("WSAWaitForMultipleEvents..."); +#endif + } else { + break; + } + } + if (!WSAGetOverlappedResult(handle, overlapped, &got, false, &flags)) { + NTTRACE("WSAGetOverlappedResult failed %lm"); + return __winsockerr(); + } + return got; +} + static textwindows ssize_t sendfile_linux2nt(int outfd, int infd, int64_t *inout_opt_inoffset, size_t uptobytes) { - struct NtOverlapped Overlapped; - struct NtOverlapped *lpOverlapped; - if (!__isfdkind(outfd, kFdSocket) || !__isfdkind(outfd, kFdFile)) - return ebadf(); + ssize_t rc; + int64_t offset; + struct NtOverlapped overlapped; + if (!__isfdkind(outfd, kFdSocket)) return ebadf(); + if (!__isfdkind(infd, kFdFile)) return ebadf(); if (inout_opt_inoffset) { - bzero(&Overlapped, sizeof(Overlapped)); - Overlapped.Pointer = (void *)(intptr_t)(*inout_opt_inoffset); - lpOverlapped = &Overlapped; - } else { - lpOverlapped = NULL; + offset = *inout_opt_inoffset; + } else if (!SetFilePointerEx(g_fds.p[infd].handle, 0, &offset, SEEK_CUR)) { + return __winerr(); } - /* TODO(jart): Fetch this on a per-socket basis via GUID. */ + bzero(&overlapped, sizeof(overlapped)); + overlapped.Pointer = (void *)(intptr_t)offset; + overlapped.hEvent = WSACreateEvent(); if (TransmitFile(g_fds.p[outfd].handle, g_fds.p[infd].handle, uptobytes, 0, - lpOverlapped, NULL, 0)) { - return uptobytes; + &overlapped, 0, 0)) { + rc = uptobytes; } else { - return __winsockerr(); + rc = SendfileBlock(g_fds.p[outfd].handle, &overlapped); } + if (rc != -1 && inout_opt_inoffset) { + *inout_opt_inoffset = offset + rc; + } + WSACloseEvent(overlapped.hEvent); + return rc; } static ssize_t sendfile_linux2bsd(int outfd, int infd, @@ -90,7 +133,6 @@ ssize_t sendfile(int outfd, int infd, int64_t *inout_opt_inoffset, size_t uptobytes) { if (!uptobytes) return einval(); if (uptobytes > 0x7ffffffe /* Microsoft's off-by-one */) return eoverflow(); - if (IsModeDbg() && uptobytes > 1) uptobytes >>= 1; if (IsLinux()) { return sys_sendfile(outfd, infd, inout_opt_inoffset, uptobytes); } else if (IsFreebsd() || IsXnu()) { diff --git a/test/libc/sock/sendfile_test.c b/test/libc/sock/sendfile_test.c new file mode 100644 index 000000000..9b202364a --- /dev/null +++ b/test/libc/sock/sendfile_test.c @@ -0,0 +1,71 @@ +/*-*- 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/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/str/str.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/hyperion.h" +#include "libc/testlib/testlib.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(sendfile, test) { + int ws; + char *buf; + int64_t inoffset; + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, creat("hyperion.txt", 0644)); + ASSERT_SYS(0, 512, write(3, kHyperion, 512)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, &addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, &addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, 1)); + if (!fork()) { + inoffset = 0; + ASSERT_SYS(0, 4, accept(3, &addr, &addrsize)); + ASSERT_SYS(0, 5, open("hyperion.txt", O_RDONLY)); + ASSERT_SYS(0, 512, sendfile(4, 5, &inoffset, 512)); + EXPECT_EQ(512, inoffset); + ASSERT_SYS(0, 0, close(5)); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 0, close(3)); + _Exit(0); + } + buf = gc(malloc(512)); + EXPECT_SYS(0, 0, close(3)); + EXPECT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + EXPECT_SYS(0, 0, connect(3, &addr, sizeof(addr))); + EXPECT_SYS(0, 512, read(3, buf, 512)); + EXPECT_EQ(0, memcmp(buf, kHyperion, 512)); + EXPECT_SYS(0, 0, close(3)); + EXPECT_NE(-1, wait(&ws)); + ASSERT_TRUE(WIFEXITED(ws)); + ASSERT_EQ(0, WEXITSTATUS(ws)); +} diff --git a/third_party/mbedtls/ecp256.c b/third_party/mbedtls/ecp256.c index ebbe25e17..2f5d93bd7 100644 --- a/third_party/mbedtls/ecp256.c +++ b/third_party/mbedtls/ecp256.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/log/check.h" #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/gc.internal.h" @@ -26,6 +27,7 @@ #include "third_party/mbedtls/error.h" #include "third_party/mbedtls/math.h" #include "third_party/mbedtls/profile.h" +#include "third_party/mbedtls/select.h" /* clang-format off */ static bool @@ -53,17 +55,23 @@ static int mbedtls_p256_cmp( const uint64_t a[5], const uint64_t b[5] ) { - if ( (int64_t)a[4] < (int64_t)b[4] ) return -1; - if ( (int64_t)a[4] > (int64_t)b[4] ) return +1; - if ( a[3] < b[3] ) return -1; - if ( a[3] > b[3] ) return +1; - if ( a[2] < b[2] ) return -1; - if ( a[2] > b[2] ) return +1; - if ( a[1] < b[1] ) return -1; - if ( a[1] > b[1] ) return +1; - if ( a[0] < b[0] ) return -1; - if ( a[0] > b[0] ) return +1; - return 0; + int i, x, y, done = 0; + // return -1 if a[4] < b[4] + x = -((int64_t)a[4] < (int64_t)b[4]); + done = x; + // return +1 if a[4] > b[4] + y = (int64_t)a[4] > (int64_t)b[4]; + x = Select(x, y, done); + done |= -y; + for (i = 4; i--;) { + y = -(a[i] < b[i]); + x = Select(x, y, done); + done |= y; + y = a[i] > b[i]; + x = Select(x, y, done); + done |= -y; + } + return x; } static void diff --git a/third_party/mbedtls/ecp384.c b/third_party/mbedtls/ecp384.c index 0f46bbef3..8b6a6dd94 100644 --- a/third_party/mbedtls/ecp384.c +++ b/third_party/mbedtls/ecp384.c @@ -28,6 +28,7 @@ #include "third_party/mbedtls/error.h" #include "third_party/mbedtls/math.h" #include "third_party/mbedtls/profile.h" +#include "third_party/mbedtls/select.h" /* clang-format off */ static bool @@ -59,21 +60,23 @@ static int mbedtls_p384_cmp( const uint64_t a[7], const uint64_t b[7] ) { - if( (int64_t)a[6] < (int64_t)b[6] ) return( -1 ); - if( (int64_t)a[6] > (int64_t)b[6] ) return( +1 ); - if( a[5] < b[5] ) return( -1 ); - if( a[5] > b[5] ) return( +1 ); - if( a[4] < b[4] ) return( -1 ); - if( a[4] > b[4] ) return( +1 ); - if( a[3] < b[3] ) return( -1 ); - if( a[3] > b[3] ) return( +1 ); - if( a[2] < b[2] ) return( -1 ); - if( a[2] > b[2] ) return( +1 ); - if( a[1] < b[1] ) return( -1 ); - if( a[1] > b[1] ) return( +1 ); - if( a[0] < b[0] ) return( -1 ); - if( a[0] > b[0] ) return( +1 ); - return( 0 ); + int i, x, y, done = 0; + // return -1 if a[6] < b[6] + x = -((int64_t)a[6] < (int64_t)b[6]); + done = x; + // return +1 if a[6] > b[6] + y = (int64_t)a[6] > (int64_t)b[6]; + x = Select(x, y, done); + done |= -y; + for (i = 6; i--;) { + y = -(a[i] < b[i]); + x = Select(x, y, done); + done |= y; + y = a[i] > b[i]; + x = Select(x, y, done); + done |= -y; + } + return x; } static inline void

dVt>t^d!*7qmMdVia(>hsS)S>LXd9E5fuu3*W@1n}ytXO9M=9z4$&S}}uYdNxEk9Wz2AKE8# zxRRxET(e3x;=m5o$iP6E)2bz^QEn?#&H+9umtFxXR|k95XyXy8F&!;bV=Apw?u~}X z+zaZfJU+IQjZOcc^1M}3HcnknHa_*0Y(m6)nb-PjGNsoo*~A4eRFiBoWRu5VQF)te zl=;{kQcW>PQ~9<~seDW0RDM+~w{^LJ!p3eW`Y+e2T_rZGl|I>dp{->S)fA{gL zS83eAq`ExWR77!8YcAK~dAoIap60)A7keEXU8Mp1kw88?kp>Ln1;1*>Z_EAqjVI#& zhkNITe`O=p6A$4Q|HSWqH63qwSwwL!{TaERiYV@>B8vN}h~nNVqPV|`DDJT$iu?ztk0`>u%M-YcTG|B5K?!9OGSVG+f>_-EvPETXt4izx2PB8q#nh~oY% zqPRzkDDKlDihH$);(jfnxMzzf?%N`Yd$)+<{w<=ohl?og<06WC`OnDxTtsnC7g5~T zMHKgT5ykyoL~)N7QQYT66!&@&#rd=eKO^&#h+>WsQOr{!in&TeF<*%&<}4A#yd|QT zyF?W8mxy8x6H&}#B8s_8L@}RnA=1Y^V`qJ94Dff=R_29orq$-6H&~0 zB8quWTx(+P6H&~6B8oXsL@^JFDCWYSk@-+WF(>|v%!?w5xlu$hKZ+>kND;+6DWaGw zMHKU;h+@tZQOuhnin&unF@K6E=1>vEJSw7?OGOm(sfc1u{TZ28MHF+Zh+=*fQOvO- zig{KpvrNyNF_b{~4L%MHKVAh+?i6QOx%uiaB3IG4G2g=6(^y{4b(d1BfWr0V0aEfQVu} zAfi|kh$z+tB8s(vh+=*4GqOeyQLGbwM%D@M6u5J8Ch$HDApSyiZzFbV%;I4SbK;l z)*m8@HHe5}9U`Jwi-;)JBO;15iHKrdBBEHEh$z-4B8oMNh+>`cGqP3@QLI-)6l)d{ z#kxgAv33zrtY1VFYZwv5Iz~jXmJv~`XG9ch8WF|1Mnti;5mBsfL=mU)uT1Z5(9^z5s|J(omV>$G%rO@vMk(N}G zrIHm(rjCx?I``;oW!a^pV;3v&s^-6co&C{yQ9v_RT!+O9j1KrOzRq^^378ZV%Ad@= zzka+!tX}h|2>Xe;yS;lq8EBZ+u=YdsB&T5ur2#8;H19XOja|cU6`E0}hkbpXUAois zpyL#Wm?^VHO-$X>yYZ1C_f{zWCV=s$tM=R&-ICyYnpl5F8`+ntL3VR!u zf3Z20vgw81*BYlE8->sLJ~yRz;_bAc^z_-;RYm3Vys8$Q|8X>NMS$Aue#D_p>eG9g zR1bE*S;$D77FAKV-9%4N{lHKv_g9L?D{y78ULlY{4_bULx? z#-yc>r_XiSaJ96~(Zc%&XBRx|d_J~k(Uq?S-TlwM+%*3E(Kp-D>?@zt^}F_$}B_htM=Mhbx&wxzBKKFLq^xS^LN&WZhkA} zs(I@C;y*lNZY9LV9^A0;!&0Av0jaM_ZjJ4cd2h`4>2B9{yTyL{xX|WihRgnuqx~K2 zkG!k<+;6(eiI#`9C(e)9aj-*f`o}Mo9;Z{n-_O}~z^e0=6=x26Ucc&f;PX4Z4#&f< z4XZKlz=7upHX&b6wp`$E*KBOR8yAfGr0w0|_|((r#gVoD?8`As>Uwb4=(m3tHQ#vf zg+*HWd6$WvhZ+{%e(13Oi|MWOkJpZ57jK?2vU%3|ZS#F@ZcEu7kXhaLeNe4Y)606? zdb*~as$W72!>Xb`ZXF!Iquj5cbkwcr?)x8l=D*3E_VGhhqq%!twwagy&-G8uivm~A zyZrk2iO`;>yDfC6{neF_iD1U|7;nidnzU3 z{?PYceUJD2amF#q?%X29mB4X*@1A~@op!Xc|wK%nM{KWL9Ka!7H7F@ev z)pzFWfI3He6}5gd;XR2(TP{qbn(3K>W{+iQwlWcix%iKt1-K7K#Rgmf8Y0-Hyl*slTmjmU(2DE zm5rvmR;l)$I`RFKO&68v?r9W1;n?g=i^`VzC$@-RsBF5p%pkA6+T6}1@{lxL>+9mF zwe$XP@VENsboUyiYhLc3Tp9aAs-xXUH`SoDUS4bUaJ$Ws{U$d3yR6}!(eV=|Zu+}y zgMZ@q_=OXjE-h=a$0vT9mZQ!I{nR?8&GM|(GwohRKG3?VbD~D7QNC?~+l4nBTAWLj zW(%1P~Wd@o}+rJo$dJOHTT{6)J$zyYLVxv-XEVn>FbKA z9rqmlYWr8uRs6K@$&+#MagQ8_4Sm=4S*HOPYn}a}`|mfEpm6uqtN1~$J9Yls@1TtE z3G?y@3h{q^+r#OWPm}DoyB;mAziHB@A$uB4((W9y!Zp6>jutbtjJwY7u&jTlZO(?5 z^FnI*u4vlgz$~ko16^jEZC$G%=ZJ;zl~g@1$BV1y#}2#EC$aL|6u0rNVdFp8&b$`2 z=;6EXr$#ytd$#VrO~jlRUJ>ud9s4o!a@nU{!Gn`J{wO&)*EijM!^x_Rb9+@Kblf^) zjAgTo5gY6eyLMPw-mSyHcdnzNd+f={7l~*1&^=#H;bkV2y^5& z_3ou!`;A5}nA)#>KlSBL>o>jr((L0JpITS19r!Z0;EM6pH|GNWF`s_mi}keiohwJg zj$AzX!-Hk6cRc$#zpk}n-2(f0`$u|p2}yb!9ikYY-)P;)jb~>Rzpz|;`D)CkyC37O z4}O;9yS!iHt_98WyM1sw75nbvnpstjKHKJJzVI3C(R0F@=esS&m&P{OXtZzL@lo%_ z4=@jHpLTpp%Rlz+e`I#0(q?m6w~42p1?<24b=;ES&+g?$ef+Yo=FEpVjmM9lFlk}o zgdLq5o76vgY|!{i#U0}kJ3006(0li|(Behnx#nNK{II_no78ac>knIgw6Pv@sAtoI zhi|XxT5#>d^{kewyJWroXYjZ5^lm?57ryo==@$BIOJ3;q{5{(~yGDL>d|`BbU;leE zrWS_AP5V5p+0=Uh8>gL|eaf)=(a{ZThHq*)&#lJm996SnzAs8fcXK$JvF+y7_cyMt zc4>K|Q`g6im#=RPRSY;b^;YKIQx+$ePrcu2T-sFE>4Ast4!S?Tw^Px!q%p7SPEdKy ze&e>IS-&{XxB#cuM{S;^$35Fy_oBID_KfpiKb6`%TeB#rwC}(%@yXX3gvJMD{8gjv z%Zf`+Z+saya)H~O=4H+?38po>wF$|$Y2Q9fHEV0Z*wD*4R`<96vv2M>^Z3lD1-o{h zebcpamZghLGyRPdrf1#wXJ6+{4+{F-?DDjRDIxUv%j<{K_t$2D7FWX?IHxP9F2%>bt@%Z(qD!rai~>?EV{z zLt5MP8@AgmQ8C1QX6##!1KvH8Ms4&T=rit?@wEB)q|FWS~5<+8)DEj!J>tQkK0uy*YD<1Qg%pC@fe+1+(_&%ric*25=QH{HMI z*_51>o&!e*PFw6SFMrpI+h5`i&(*5_>h+_8Wmff)0nypz)!z17rL3%3|0KqIJHGc@ zz^CM17rUvm78qre$9%GBebLdgMP2XFalgGEue{7@Ynr(%z$_&? zXNYOvzq?&&7xoZnes8`f!XHhSEup=ZBwfG7I0m8C%d_k-L=ZJZ|El& zDa=$O+(XENA-ML*zdSy{Pc!5ED}a54&O7r+Ot+!;~Sk5U-NTbiLaa7 z=$hcG$X6X|HGSaMrQPe6HNVj(Ax!aJ#m{&R{2J9=RwlnOAn|pp8-o&7Dw@UgGhIEX z&+^pzWo>RaBy3jnjag_~C$wmdX{XR4e!y$+i501h$~xR|PdKXZk9lBvb?}Lmsm5iN zHGGs3GWpzV;-1YwLcM^YEot!qGSEKMdUs05%Y}Xz4)Ony`1Th_iE& zb1?E=t(>co_v+-ljub!Hwdh{GoK6o@D|JI$tY42`bg%JNGd9NBF zVe*U`ArbOk1|d=MjAi+i*_E-I&rL0_|D@-PqHns#N2&Vi9UrZlr+3^#m8o}p{GZh( zmesGq4O6q$q({7Y^t#?1n-qWJpp3wn`=*)tKkEMbB-TDId_Y_AB?FGI`BWf z|Fp_!)fL}GRs#oaS>&x~*nfdl&&x)xJ!Cq^BG>==j{LvABVCcfeudXRKa41qm7F%Gj3ifE{C z6sh3vn(_A>`I?m(e=?@QAJY+t5X{6J%*6sk;w;YLF`l3jpP@q?bx|J;;Ew>LAswcC zsbYr3Sb|f?f+0WPP!mB2#wn<2^$Z+CF7i;o57V{dqvm41A*Usye3*&P^(5&F-q)9; z5BLQB%A^!wMtOf(O#B2rK1MM{6SRa0%+MN(uo%m*9MMo=1LE)%-=NioW`h9?Q3rKV z9}QrPCg_2l_!Bmm0$+q;HvYoj*otj9id1~WCwxa0^xE=y8|uOc#%KaLTEPPC(G}g$ z8-36p;!2o_Sy+j^$j41|Fz53pbVm;i!*B#47+bIv8k|NMUZ5JXc6^pMn23=>_&f#^ z5sXDpApyJb36eeKg%xaJhlPm5H%JOe+K2sUI+WEXOdv;Vw1EZMqcbew07pzmAVM$` zVF<^3EWjsJ;v2q0cNlGox{zT2obVRXaM}S*7~nueCNx;;NWG9eLXwW4{z&>8rZ9sQ zx?n6kp+XGyVjl*$@HrWFj3EvD$%Sl#AE?I0agwwN$8iE*@eSD%Bq;~Gy*Ll)6S+2^ zK^}%oqMW#a64*?peQ^lMI0+4I`LG}5sK7*D`U|&F0=XY`fXP%|qX;Fa+if&M1B35BFPU8%&;|3n$5kBA}boX)n zp&=TfHQJy%dSDO+!xf|94Ij)xD59_oo3I)CupcLoiA%VQBHYCbyu??0L!JGM8MHu4 zSfLB-&>v111tlh83VacO=?KA0gkuioV*yl%!FlB2F)CnpfHYxrknxBq2*z_%U|=fy ze^C_jrYK{`i&zz#+iI3J!+LOYlDkl&!Lc!UZl zZjvv85edaD-eb^hB1%yKWdX;a1QqC2NIp=&31gweBm`qE)HsY}WI=;{cX*FGD1lT& zxlneObaC}Q^@Z63%89P9gAamXS~gaKh9lY=;`jcn|3*;|g{-4h_CM zWBYUF-q*Axx|FjYW1tlAEfJ4U0rPj{3lAu<5-O~P8fTD)W*=!|Siugvkc^|B$QL!f zQC|#!6HZpK9o|1EH|9zF5k++8v8#4Sfd)-@z^q(6WY$1SlEELrSZAmusj;M{mZUQ+xAR-?nFl|Yi2u36>BM;l;q>sjCmQcJR#NV+JP%z1F1!hW1X9twTgjy-;~ z3);*i9k@UVUj(CbDBE#-Hs!_iFt+2=eBNW(Uz8hxE7*?1NJb9w@OL!(k%VNNMIPQk ze4&#ULpdN@OI>hi9mnAu^6(zgdM&9HtS|~noIxJ?$I{-oj65vdK>HvW8o0)B{4c>o zA@Ua$@g*uSH=gadgAyFtL>j2tLjGH|qv2brR=cBh;w1mvk`?k;q0KdLQ681S1k}Qb`Z;4AOwxG4e&m z3F;5Gle|X|g3WRPJQ7%|~B3+#MO#V3Vh5fMmPP^j+ zq$>L52kBv84Sqxu9<{Y4C0tC1m}jQV*Lm8~O>=Fj`(y$YBk;Uz~`7 z5m-y~z&;kc?)1NFRgXgtvc^jt%993|6p%Gn7~Y6(;s09ejjjt1X?iBYnI;1#0%^ ze3YXC4F`}9l!G`AN1;LdU~NeaUwgJA8Y+~c0tyA?!X1>rZW!-za0KOti!*6r%orlh zxKj?8ds23sMjkxIu^*oxjn|gi!3t_5BSA^TwaFZhH{RrrIX<)_=1w8vlrQDR=4qUZ z3&_J~NdD9tGB`ttWl*730NXJI!8m|qWI=;6RKR39?F0un;Q>l8J&^No5*j!Kkv@(D z^B%QANFP(eI1Z0cfo9>Phjw$RFKl6ln=zz|w6&a%hp0fkbsUd*vD6p$<2Vkc@tlvf z+o(6%sdL4_x% zK+|N(2L+rE3pIj|ay+C|(m^mHQ3WYYTQY_mdH+OPTJx0s_@k8Lu?8v_m+>C$ zVFf2BF$!*rxr?7&VO!eM0MByw;Ow{RPe z@EGN&z;{$ZHe5%l2RraluCxVev~(mN$ef8-2Q?CqjLXPFtP9(5b_(yY*O&cp@uQxY zieM~6BrYKjkET)%#7*OTH1#JRY=atU&|pXadg zCzKd?n)Bdrh6sxbq>CZhyvI98IXcn+I3Wqiu(-%^xQlyuh(~yiQoP0+yoYp2N2&oC znnI4zP@?x``US(_gj!cQ4&7k~Hz=_lY80ac(lw65QD_ibz8HXVQobEFmIwOS)pfB-eWR?u?)+x9cq-L0_OaDQaki%t}FG0QA^4LJ-M!= z4`(Q`s+F#!LL!p!6czXe$y8SgK_vDf8I4<0F8CoBo1jJ>O0cgD$Kf>c(7Y|jV+B;u zH>VtMffAn}wbPY6po9u)=(XoKOzJ>BXl6+|m;fbGpg|Wa+5}Sk8mMp)d3fKQ{Cnt1Jz$5PJ&E`Msh6(w2jo}}H6~k=4$dGCvwQ1Gk+_09Jh7ww zFtk7UVjPrcIgoTPY!KUV1$h`dnEkki5`2L)L{~b1WV9Pjzhg9%D0d)zv~VOnSilOs zVFw=sBN-Zu9l?2ci3%8uq+QVqcJM|p<~Zp}kr+CP@?jc+@i$Z`MhWJ+avU~5jc2I9 zcSxgkrBl$L2qhRehV)Q`yLgNW^m8MBEOsY-n0Qhjn2)1A7&L+NknBZz&{tA!tbhvb zCvqGXL4`4ss3-Eg$rph>lplpC!MG{x$6H^@>BoKq<0v#JLj|O%9%SLoz1LqI~$g zkTON+O08jqq`%mY2}`LDPC|ozQKXAJl)z^h=VK*QxP&~^Tu%L)DxkNO?dSzN_#+r;NXJQNaDN-+K!@#=8*8D4x041EPyL~Nfb%f)DD}ocB;#Zn z<;Fdfz&f4u5Cb(1WN;iFp#na~NFS|^Q$JiiLHhWRNq?Q>x;({pTszHnXrCcp41g1A zoaH@MLxt~<&QX6z=gA8iw7)<)h=K~;vndz+5R9fd)E`6OgxQD`uP@RL2t+VyUM4>r zzCzk)dX4j7kw^MSKr%{Efg0C29?nq0`v&P?50dc=70}A(IJAZpA`pp#NXE09y3%tr zxkY|x2P-^51)Oe^K0*pO58Vo>A2Oi9b(FyE4##08W}#sb>0tnzu<>hF)t{@Kvk0>`zK?B3bv=MC34+n7wTc0v#qs}v3sV-W=1amPDg}4Kq=cI=} z&=j4}88+yL{ulrkxWWzY2*7mcmU3;O1>^|DY(ztaLX@Ci8Fhj0OZo#w<>ZS2@2DS^ zeB^js`>HGDq7)VATg7>p{Dbqbyqe>%Thf#E;0c~WuB9hgVG@GjuB|62QAbyguaESk z_~v?&8Xhg!j?GY`eM`>6F%vyWgDOaJJ;?!1NNdG*Xq%D_CLtJWp~e~Hp%^77M+K@O zndwPJkii6USi%anu)|0=!3#`hh$_ygIwgH7$qo21*##nCSS;40y!*U z1zXr*B%I&{C4vx)2t*jE&XMuC#ex_PsCvb{f47RMFx%`6DN@c&2;)N zkmIBDqykv2(v!NOJ9?o6wySAdxWOHs7zZ!NqbZk){E>_!xC-?;J?T73V6vX$-~}bN zLXG>7$5L*nMchinPRO=%9!l^MlA7{EgXd80)RUqh?b4HKKn{}xJ;@pa;fLucLG#^= z=RNc{N?@BvePEVEM63Nom>nQu;338*yr4uDG`M@1dM4AZ&|u{eJ!uuPpg{p%rRhm; z&^?{&BZK^~@&xbCQ0{ZIZw}YPB|RxAm-`FSk&PVO#XY>n8))U}Nd{Z$V zgFE~YfCX5Hjo5@lB;hzt;4-e@4vO#;&rpfasBxYA&7o@((F@iX2`Bhq3YOq6>_P&LAO%-(4fpW?@9_aT`FfHr8lf@F z(GI;}4M&WCFZ>XOa71AlHewTwAO%^_;5u%g7$qo21+;E57oY(e!US@3LwAhE82Dfc z7GNQwp~7zL!9O^KtGI^eD8)B?N3C0WQf)LqL$p9kbU|12#{js%6+W1P2t*Ii|hV+(t!vIE*!5B?o0y)gl4wkS&FIdACcCd#6BjE%$xWfxd z_`(lC2u2vf5rIfVVHu*KLLB0;9cm;Z35Su43>-riG{{B{a*>Au6rvaOEaDzL%poA~{5QK07nIR#puW@` zt{9Dyq59H8G#o}b&q{B9H%4E&4{djSNeAsbC?`gY)t5$MmZ!cHiWSfx8#%~D9&E?a zf3SxFBjE%$xWfxd_`(lC2u2vf5rIfVVHtXP=}Xq=r6eK@;mE)-)R?F*8NdiK7^4YH zAct2g14#*A_=&i~Kng|}!V!T;L}3}Cp+X$uu^nn8A_<3)j0_w@7Bt944swx)0u-Vc zC3ucfl%oQb_>5{uI}M~7Fn|$cFh&!YKn`=XgC(rc3)ZlO9qggNNI1a_?(l*VzVJg3 zf)R#rL?9ATScYh*5QlhdhZ>1U!eJyM1ILgB4YHAgT;!nug(yY|o}(1ys6Zt?qZ-mK z>JI}LK?Y+qfeGX=M>|-;3cX+rTiC%K3XFsk+~5u`DB%k~1R)q<2uB1W5rt)lh6-_r z$9AZZh$I|FGBR)sS|hTCM#2eh zaEBL^@P!|O5R5Q{BLb0#!ZJidg*e1xJJd)-5)LC7890V4XpoH@2}~e|IoiPzR_Fz5*uoC>P+%mS;0AYiK?z^@Aqc?;LpUN3 zi6|^XG*pN~JhnrPL?q!bl97R9$btsh$U!dhP=G=dqXf@UigHw-5}#2GDUtfa07j6( z7_P$(k%av?fWt_}QKTXR$B>DW$btrEaSqwY!DZy30EM`VdniT;9^(m~ zqZBXk>X!=QdwfPUq;&GhBp+CwWZa?`tYHf~*h7KgaKK17!42;4f)c*)LlA-yhHyk6 z5>Z%&l~5rL@z@MC5|M<%NXAiUVEhmLfiOg336>!mDy)MVl@Pylr2_*PK?Y+qK{GUm z3FHVvB$i+qqM^b%#A7?u*o7nff>Kt)f(n#hYqkpFIb~5>@W}tjD!>1;0`Y+ zMJy&F0+EP@8i~-rxP|hTiCc_Uwh(rNOP+UsHbCjYSQW@t# zi9~2%@`C(e@|6fLDB+7pMDR;(k;sAu*(gDAQzA^78A@`P!wz0hipXE9Q~nZ3j4&e| z97ZxSpg~+~Lq5+nl(x4c{-PlkbRc~^M=8otfl7QvHKdM)QVkfu2r`(%3f*80Ti9VF zoZtl|Cc_Uw2u2vf5rIfVVHu*KLLB0;9cm;Z3Hy-ryP9h5$WFrTck%s~lq8KH3 zj#9it1uF3w-yn6OelUO$WH3e(G=m)GXa`GJp%<)S3p?0Df#Gn#NI1a-?(l*VzVJg3 zf)R#rL?9ATScYh*5Qlhdhx(T!;$b8s1ILgB4YHAgT;$;{O7I+|C`SdpLF!DsVE`k@ z&;Si#j3#J?<}iUAW@!CO8=}zm7jvS}?iUN9(EgVWB3cdJWNK|N893RL1Vsv-5{cWa@G8Zh|9kSP5A_qzptx4`cf_}v1(Ti|yK{BD8Y zE%3Vqez(By7Wmx)zgysU3;b?@-!1UF1%9`{?-uyo0{?Rh+?mRc%5z8?w1XuE!vTds zJP*mA=kV~F$9YL};0zv3CS5{4K8)nqN$5CCl6X{))KhGO4v%oSMyyG!i-t%Z#ItgU zODF&9A&slqztfH9^bFuRgAqKRX#vOe=ea?=KRkg)^}uf-&lytiEGp8sp2BmV=JPzj zi9832<4b4qtR9}nHg+`MhV_=D1EDt>?wB zig8XF<+zF`_=bksJZBCA;E9=t0>4HtxlQAFZ;?Esi?sfs?D4bsI|h_{I@`BUu6wij zF#y`}%s{^VpvQBa;DJAe@LVop8r~15PUO9Ta#+)d({y+y7r$;JeiU(T4W8%6N7{?= z02gcWc$r!}%g<5b_cuwiHjmN6c>Ilx$i!7#Mj|et(`dHkae(@k~Ja_z>-BSeJ9zZ%I5)9Ss)oj5s=eEq!1+fxd9zSw>1p+Rgwt z$$3j>@C+sfMHl?PuLg3zCF}Gn6c!~xGAMAZwz1`=^v$B>M=a4khJQJpdDzNX^bHU^3b8b zTF~~x8QZ%U<7Ko>9M{Wq%HYSipTC&v&X336&<@8r?xnWGNnCsAfu-RW^(p zqMt79-GF|AKW5`EoDHT=9cUx;pU9Y-$@rmN3~8fH^y^vjc|{#xpi4u_1|Q7Ea>QdV zuAmfO(54Z640~K1&9mY#g1)~^ox0LK6Uiqlfbm9s0y)Ntytf$tuXVlUDhSYH&ebR<~C)n3u3iafi_0-jgZ2<^F6k@R*hj1GCcm?rXRHGV<5m=xb z`e7(0A`mNa1ev&q0z84X0nesGXY|1h>Zc%1VhrW-+J)D~lzk#mNt=ZcBe4uBY{LPV zF*fYEW^;L6Mw@Q-;#Y8J)8n*L3&z?|>bjS9Y{FRU?o0o*d9D?4sPX42bjW{=&a#zz2duiM4 zLl}Q-uQQi%L+onIebSBb%f7bu{Mr-dFs9d$$Nr&Q^R&G#$Ervtn7%h;pDzC5_#<2w z_Kc4_@_!?aqkh&t^aJCfl6u!Lq;433W299|dkr1Nb3y6rD^tmjJhIp~dy)ylv27g^##3kN{ebJlmon;-_F~E#gFVPV4vO#?gBXYBXvfdI9z(gbX#XRe)0uvq z*o5bOGKP=RF1vX>p6zFehHN`X-?sdNUj;`JY4qowF^N8LpE0_x@<-5$&KrKhg4cg@-gfFSls?vw=2yzQnEOFb z#z5hC%1Ao<=(jM=AKH|$qxZY{Zn0$MPia!fAfn^I?DIrN&dA8lX_M@+#YtiUGh!vz%LA&g1O9HY<* z(<$>H%D0vLY4PH15k?en>B*_4r5>;?f!Kx zc{9$saQqC$#BTQAG+~Up^DN&1tQT>dvT4}ApY*y@uEm_M&$dDSj4$%_GLtPbq(rVk8@6w#t8bhf<8G--wdD(wWv>X^4-Moy=TyG9MhHj z+^PFJSMuil70R@cc!R$CM43#&Sew!&)2Qz#C^_~Tb)7;>4W~BtVwCleUrE@sFMTZcRg*=m)D_Yl$-tvA$@+u@#iy{ zrv$Kn5>*4=Im=@`Wv?eW_NTC~ zaVX~|)@A;q4DBh~c+P)JTP&h~no!QC1DHo?iwfdW+ItaocA-5^(?)a2XJ;GAf|Ho& z%zYq|G0zy=(w{kj^IB4$%hYGLJ@-7$AHA5iHDdnaSWk}eZ%aB@gKpGk0_~PUTt&NF zrcCji_mI2?z>_wqSC4fS>1T^H+3!hTxY15CD6bvqucd$MQLg6H_fOL3z;UDLYcJX+ zfn%4LbC19-97QVa8%Z1Kljj)f|6Obk=j##5X4;P9IOjQSR7#z-DBG5)%pt>>KYSUx z^hZ2x9LIU<>G!D|lg}90Kz_F=Q!ssQF@?0LYqFB-mpq;`epZd3o?Jt3?3p7-+@8XwT_f8@zu+KBFgb zEm5yor1zb)ib(Se+<><{TvrWfATtxcJL-mU(TyLjsE306CdU?2l{~5 zVU#73J~X2q4xDqCwwg_u$5EEH98*dkz71flOTT>KI0LSYaNhIFOHw9vT1L9t$!7`e zvlJgXvPPmDHl(N1iE+sOV%oHbvLB@#+md$|#_MG2c#8ZQvAv4%B=1kZar|8JYtH#s zX`jx6S>NC;+g<%AFV{c{?bVF7tws9xsb@pR!$Qi@i*f}G;$FnQ2aGobW!*%Y%{k^g z`xnt(Ln!kK`sgCD2k91*?*_`ToVblPHKAP3IX|EN$fe!H^X5$$$4ePcD)OmZ$l9DS zu$TTwrabvh+n85ZP}J`6_d|C((FfGs~C6PsmFKP z*ND32YjF=-#Q0`=Thg4U&HW4tyy4tc>~G5J$mx`i{BM$HC;GH5@i=w8>BicE_83aJ zPLHLG+;3L#el-0wf%?y-?p;XtJLU4BU)zw+DB328I!BX60qKS__A_adZ$9Kj8#UBr zJ*q=Jh?S7^xCan$I;Xws%=xp=;oIjT zL%2p<-}XG@1!H(B?Z5Ji;0auNTsOGh!Zl0zUpHy%iHx1+cfWvfbG`p7ffs*223@=v zc>Vhq0+S~pZ-D=2o{Efp8RLWI9{XjCk?TviZsXeIdJlN?mB8kU-vECF2VcOvbmsnB zS(nH1{RkL6{Tg&cyH5h=|N1s;1nBmyuc9B;@@=f)%fXo{>-X73=Ee9w!TO)${a1je zj|Hw@4sJe?@BcS-zXUk`0BwIaWsmy`=D}LOobmtWo8a}p_D5Ljr||qeR~Z}qzlQbL z0?v2e$=K`YWBZVh)}Fq;*uoId_2VuQIk7#@3;~&w>-5qKzA@-H(7T zf5rSh%=rH?^ZF!p9z|b2%Qt^R8|PW)576(A0;fLjzmN62nYDNd?R_VB^4pC4Ex_Qv zQTA@?e28)UQ^x)t#_`R}<2RZA%Yg06c>iVKPlbMF8o&t}`*ZN^yYFG{&%mzadJ^z> z3jKckv6Kt{fc0hI@@)El+!uh8&qT&^EpYwN0pGK3-}N|nIakK}{le6wo$ER0 z;VE1@Tm!E6bA5#C$2Oi$?RoLIuc{l6w`@nOG`xk&q zgL}c%HR{~YoPgaMw~z(vd`q8ez;yEsl+(vS##++eU(omaSc{8&^s*Z3qcQRA;hn&p z@4k`tR~g$6(&oDu%SRc%H{x z8SbCKx8FVlk7)DjnBxnncb>AJ<@yZYWsH4?HUBX4ong(smHGT7>wKGN_ZaYHG0I*>or}!>I_vp7=J`0%xjl*`eyq2 zB<0`4cL%inA8BWqZ=b@Pp9apnmwvyNcVERg|8I&6zX6?HN7sR;zMr=BTPCli?s>-h zZ1~E@fJv3VKcoJ@lU3&Q4!(OKV|zWYcqL`;2d5g8{T6?30SpJhA?tmS3Kzp%h}ywh*&{MCK<4w&N(V}0Z20W-$B06f~XeJ|I|UyW>} z{JW{wzYV?w>|RCPU!~8dvo2fIe+Fax@By|vef&@UzJ_`J@uT5WT<_)ETezOc#nGF| zf93jJu7ASwOSxXh{ZH}t$GBedEa;o-UEJ$;f3EO%fr}%olAn17wkg*$KHuxi^7j&B zV_SQ26W5pX-RtfL$8UkRa#gw58kU^<8tChd$ZzT{LMPw&OvVg6Ui<~XpX=v%{s7lE za6KQGyqCXU|5|k61N4o@0Z*>a=jw62itBry%~-jHTtCV6LtMYh^&hzY9oNluXygg3 z6?pjPkB7%|-OTgwIl!3p{VXv1jbmtoHT(?MxdmkV>)>r%uLh=nHU@@Vzx*Wn2G8F8 z#pqfuMsIovIL);R?bKchjJZC}dMqq)&)i?hb)D;JT!Q1*b3GLt_#o@^H}vzTPX@Qa zg-<>mUe5I^TtC6}uerX9>(Aza!RIq4;C}BKx)}FA&Gjj+zu@`=*Xw{+mFqJ2^(f~0 zY5I5qe?Q6JU*`H!?thTKAKrweQ^3#c+U~*!aRN(e(=~=1Gm>=yT2Sb%%RJy zBD)vSOQ6#l>-|mi_3;-$-&{Wf?0$snQ}^)vCGhm6L(=K@2@{uh70o##33-@*Gw{(A%hk3irN2s{FTMApw>>6GvD#i>Edo{RPUTFK5X}z{lo0^i=*E7 zbYWP!(=T?0t=5_2_vJnPLH~>{yY=1PxO>KnLHmsP1Efa1bH2GJPA^lFZ zvtfPeT+%ukFu=6Y7eX~t){#MiV~LMD?e%+UcGw;@_R{)rcia{A#L5TtVY|NFX=TZ7 zzuw7`PCXmxE@>V!kf}@O&R=0L4ahe%K}{ii+8>OV_QXS4@3fg(7Unw6>bnet$pvWo z?Y0$ViM>AAw$C$k2#||T8M$VjW}QCVqux2JA7=)=G;58L942XfJlcb*o9$lO>rP#~ zc=2)q=nNhiJmWCj0pR+$GfMYctwFlmXRdLiUY-FzR@a8y?(G=RwJP>eF^-*`&N$mk z$GuL!vEOQfICKp|<0fS>b!&9yBZ{Sr&3FeD`r}b7<<^ULTcc6C+v@K$>&N!8T;NsG zv|I*@HH_HN02ZOBwN7)fnavG{^<#VHVM)wP@3>DoH)b0Hwau)X8Gn0y5Uj6ZgI92L z$Dd(qug>g*OTI9W4d8C{d(Ao=GT>daHOP{zS0DICP8NoLLZ)=2m233Jz0uUgOVd}9 zopG-r)S<6!!#T3 z?6i-(hRCm1?@F``y`GZWdZg)m3T^u!Z(BB_3>oB3%KZK{E zR0QpJ+>B$P2|IglOsHEw5>aMQ+GU50_)Dwa*z-^O!}e~Rn&10MSPO@3Bv&06u>K(P z)Wr+uE+^pa?r2Z9C>4s#DhBYQAf@Ukx~Nnd4E^m&g@m3&FM91bbTqbc&Fc< zpJ+7J6#*R}-OC-uwH~u%mfi}Au=Dv6iD2SBqLrlD(jp_4RKivSO4u3CP zLDGXv&dL)XQ`%$%-h89e&tT2QNW|2)VgAiD4E@5DOEdm?X+qi5pgtTSHil!7bs`C@ z+x*5G7OP}kSb|<^uc3N)13HG0l9z$KfwH0;5eEq(B~tR_voK?_1O#LVXNYDT~@P>?&I~+&~0~&(wU^Yc&$;OX|u)n#=U)7?Pa6E zus;9+&~j6;1R4u(U7?t$gu>r~VItzj8JLx|cBMZcGfEAiQKcv@-OpJ_1?F2!N>AH2 zpmOGCf71-r$nWY0Mta=&Wtkef+RN|-)+#e-=UmoCTqRT9yH&FmL&96D6L2)l-gKnbz$}BQHzjmwr7vW@jZICsi?tj zSnE~l!_M(myEi|Cny3n3OrdoEqEV#PIq;Q0AD^a# zKY*dJ=Y?sC88R9j8{d!$6Yt%iG2{$yQG5@zdZD?-9;@z+z(*VVQK|BxZok)`n!b2t z7QGt+@L<5hODQ7paI8dcjjT5CHSEvPnYt)sOd z-bO>z1pw^zS+mArGr!L@K`-G0xW#xd%KUlgoS@$rby7G=Lx9O{gt`o8bnN&0?M@g^ zCu_9=VrvB&-KGo%z@{hAU|TKnxYR?Z997LAVS#*pCUJS$-`RoSriH~FLu*l!_au1h zDJM1IO&BaISya-x#!3;pRmO1kBgC@0gf(WIl+++R8O(U(2O&Kw6yFNG5MOGkF;uTuWiZ{!Img004Z%_R`VRl%xarGf4$LfXXEJkFt4 z@AWg#IbaG%us-YyS)8z!aFt-sw`Cemq4P`)vihJMpX-y)dr*YGpEG=$mp5YKYiNbf zXQoRHoG4Nod6A*ED;LbPnES*7=#KCa#Jz$jt*K?rOW+xr5zqK}P_6_%dpKw}TA*bg zmrB1A`H6IS>zFFK-w~q@pXj-V!z4b+zS6zxQ0f7h3-5$QIh$yVhMmYNpqo#gBcybqd{Zdw2mkd$`;=YVZjG2Cw8oMmP;YVZo4Pu zm;+!8e=ar7r*PKM7|q-HDh0v#c5n@%p2V2_Rw`2da6UlP>?jl+hcEIP-9MD&8r=Y3 zVG|!sU7Ve{NLDmYrbFp@Cb_p;AMVH3m&0TJ{z9@{9~RYT?J=}weZg>Krp&ki9WVuoJPa@Njx#=V@&hLs--;VDkrW2>&`lnkK7~R1MOIxujgdw;J&5 z_EBm^S2lHV=F%020gZGxZsfIP4km*tHM9F}qim2inRu3dZbXa>3B&rMsjX8&6 zf4luYs;W7Gq_w)fk9)@GK+?pd8vS1oYKvi|*tD*OU!1)#9h#KXkd?-KXaX%62g}6^ z7cOAJA?kNqJY2dI!FZ@wv;KsSL{AuBHndVZIjzbY4%pyby{U`mXJ>O8{`~9(UxdE+ z!!{GOiO3e5)gm}#yJp&A_hUSw+vAZjgF-I6(u4T|9V1~%=u3dZ;gBi)K9FqA=6RhznW{>n^v z=Kk^p)9<`(_ko&Al&V7FUuD}REa}i&!>$?m^s}_vg9@)<@tY}^=x&|0Z%eBWc9+VM@SWWc4n_aw z$Q>La8q<1w;0`(;9HvA3vvwz)5(NZpvu>Q17403in`S%Z_vbIt-eDul-yAk#9s5uu zK@IV7)82Jwo*jEDP1gLw%j(3)XN1C?*&U`(kTLjDT zhVQXQx~*v(d*z~IB6%%*7zgD->QOBs)oxebz=S`8(U3h%oo0P|8&Gr=jHB%nEvl0f zjp0Nq2601Jn2c_Si1Fs^O}sO9m)s!mV;-{dXL2Y+zkbqz$F(zgbL_VET5%cOYKSMk zDm^o1x0nL@@En?%n!R}Lyute#^BQ%NdIp)<0*O(EC(-FnD2})3G0a8wyT3{CDkk#= zYk7sh4>vwpX|qCn#rc<)`%|-*ku?2Yqs86Kg~Z%VDm<5%Sx7akcWe$(7=mCtY;$V{ ztcZ_$X*c{KB+>!5xpS_E)k!ok7FTJa<)%rP`2K5Qm9CCewPEL)Wu(NTs4*&5EsPY~|1b~lW9hoC|5gC)&TzfPU~DuGtitVPg- zz|u}`mzaZy3OfX1?2mbAc(#y_O6(@W1!9uCmW;%0z>VOoC*8}qh<~G1xr1|Aem(uQ zFC~NZEZ%25I`+jj%yGecI?4@p#e?Nk0xu6h8}1Oa;e&%6^JuCt-sbpRD$^*W_wCTG zhQl8;BKh+2+wy1K{l0ao5$E?-In5t&D&k{k!n{gF*zV|lp_4i+hz6S7cBVdIf?AFA zYH&72j&fB2&tXzqI+s@&^bap|6}ZE_g-Lnr$UJlD@}*>O?!pwl0UYL|;ixZAX0hRM zXON)D9mi)(5tbU87AZ4x8K=8_GSoLzG)a z%|bTyPQ_hnra`TbM?5Y;LvthiVIGMK6_RVi{$ac)?q5g59VeT(U(~Kbb~Z_ku{pNY zhWqtHSuB#+=>WAcp)~G;UxP@4e3-nG8(uGV84!Q) zJ;Lj`>6%T;4M9A~PGmPmv1#bokNV=URo~a=s@&>Yr8t@u4C(0UK&96QKuYHI$`9P>+jy(Mp(M2yF&+vTr*6Dsc5CHfyb1Ob7 zEtoOF>C2a8V=J;V?4cV?Q84YU9?ji+KIZAjU3`?{=Nu0Rkyykvci`~?#Ho*un55t1 z$%}B{jVKemZ4BE3)b>oB*%xTjyEyUH)!0Izk}mPPYvwP~dpVl?{VMN;OcTnu&1Poy z$`uWQ?3J!*N^Nrj(hsBtoi|&WxV(Y<>4>xQ*Vi|P3dm!{!+JZ@2%}T3bnEY7SO>RS zen@zAKqe6R1ZrC!sVn|k%#mB@qu)7bp=<4#E=(Q^@8z-MFW8=s8N;IfsPr;B{`s=XW{wUbGj%-S)RiR^Z zpx|tc4M^DX7=~2P_00K;I9sIqaCe^cE!@(flIUQ%(a*Y)+_qNnjjscx&7g)EKgd6f zj^5Bt?&{@(p(VIXBdf>qFXpepq9FpMTk%KK(nkDUoE_Gi?IYCq0I{BHD5Q{414Ccx zN-BL1j{wqg`xxV*^o+7U@k}UqxBT90|E9!A`iD}?;F}Fk6w=+E+F0|fvkT#A{+@ca!D$m+G*s(=D znb!LlEJYF6Iq(iaQ}57kdSj7^?p@*K#uAt)>E%VeqD)>HtKZ`ejIsN{w&^*1Q?#wS z_?{P3gdziMfkM39pW|h{v#dACzW?EMglu@dDL^Ot{s$)+IMQsLzc}h&rrrI8R)@rZ zSbTZZw|>KNOQdI5yOvei924Lm%?AGs$miNbc%=d_M#<9}ohtY0#C_#k@#=EAQkC^D zaFE-GgooRmx^e?lfxzW3fI33%%t3|T$IbQuWHf5e^7#nPtEAQanr@QDQJoQ>c=9Su zd2%I=8%Wz6+q{q;g}L@dhKKo+;UN@_1{~QTPcAyaMX8T`9P9__C08eqH=0v^?gRvmqUeHOepEd{qR>At1(WVx7x-HhJt&m z^k6EenOGEZl}jZOQYR|8FlVR@Me_OFBH>CN&V`7^V1QLfiUp-( zU8NP~>gayp-r?oGLtbv~(KHHpD5iB642O9$43MH&0W_QD5tFz4MU#w`@^k6ERW?tB zCBTGQ#F3Rn%cSuOn9$&M*Mu84drixFG>|Z@cEdc3JnrL|8tolpBzmf3SB_P}*zK&{ z+d~sIWjc;zuGzfK`hpJ4%2A_b`Kz~MT;MI3h@g5Z_e2EsM3Ew>d65u&r`{ILz(c>x z*#9fX+3*z^>UQfD?pxaFzrx;1RGs;9j&z3pP%#A>VK@)kR9^W(+bn8bZME-F{PrO< z9jZQP!?Dg)g8w8-KcrSw^K&$6Rj&5O9SpID)Tmc|@K!^e`8|BGyZwjKY^eI6&Bi*{ zR%#EWNvb_)b9#TR`p}xHevYPie>>s5&bTvvC_{^NA9Q?qr6m&1Tb+l}uGf6fRzsE7 z;PH4!NMscsv_-G6OfU>q@(|F{Yd&a`p~_0Dv)v!F#p9tgn^%6&mWx`eq*~rB@$wLc z9P2)4(|M)M+WbRllxh#!p59B55HEwxncowJ&26`94r1(boJx}OQGCIYJDy;Uz*2q* zjvsxXX7GQEAV+abbOFmZ(yM)x?BQr+9>HGUgmOdWc|urF*NNm%({hwdDqGsU%OKj{ zljFgPwEc#CNg=I#yOB-XCLA2x9;C}|*t5a|3PfoG1#?KLNIZ&fC3y*NDACED^S1J* z!5**m!Nt=>=d5TvZa2L(eVy$gPkYUyq7S<99#k}wq%BY&Z!3$wi4kdlzaIw^;w2gaRp+_5By_FaU2ZoH(cvAi>Qeq$zFsBF6#Nd9Fq}%MC_oE%^vVXs zeE}M^oiFbhZ991gyanv>I)3d2#`Xf`ZhI^=IfD#x}uja&~Dod_*Z; zwvqD<$G{0B_o4tTABQbrWHmv5k$g0!^pU6@8(8G~g(AKN3n0!rWfDNa+2X$?=(x@RBP`upVJ#kC1O#9F_G&aEtmq*7g9TCaq?>Ug)RtJk7VaUG zgeMTK+E=pF?>a@8NZP?8=(KQS4hf1K+9*5_7KV|}3p|+j#PgimwiNgfpC!2tT3uVA zRUw9j={SK~Y|;4{!(ZQ6tF5iA*EqE@kf_ZtR9G+Kk1}FgF1{~+T1JS~N+^=q=HrkN2qQwo%>h9QteFNHPy2b!kuP(36 z-;ux=m$Ie8^GGX!l8a6h)k5DJHb80vi6vf{t2jiki?1rU7Uzi|L^5=i#|GHS>Rn4~ z9Ob7Ch@tvC+HYhrArn>m_{ox1wkym-fez}19qXJr!Qcs2w8${tiHTc8=-JhSTiw%& z<7&hWtbs8WDHgy4@EF^~OklTTo3jg4%R=Pz<~i63nll19?d4!rE`yfP6D4B2(Z0)b zEYz}17guuLg(uJ!iQf=glWmSH;%nQ3d90uf&_O%4X&jpYFEhdiF*n9MB4HR4(|2;? z>o%3G%(v3I$kjQUsFk^Vk9A9tPoF#2NMiA>QrqQIqlCGoRq*J5LIqptnA{8*ZD;q-w(=9kwviDlk`-Mn9?nU@-8|0@)X-9mQhny;2|8p41)95ve5@ zhTH|A5t5m#j}8k_+VHK&vck#9Vv4e~e23_4btH9T(GDyD?C4K)9({hvM!I$f&#(`j zPl7&~=g&$ArPnsrwyK-!5iBti3d1=Fbu^nc-#ZbCQF_FZFeRmdlje)$IwU4Elg&{i zBcIa(WFAgs3KrV5K@CWE!qW%$gkCBe3zfNr$oF^;#*#&^Xi!pFUqtF}uCK3cpd*UH zqWGH_#azCjY|G*4>1gl~>6J#5fT*S!kNRT{Us2{KlPAeTr;P|f^882*u#_(%6hL2Y zcMAxDJ=be<%Nr|`hhpBbsAG7I^g51B z7|cvpIoJcQ5q=6z(I7LG2}mioIpiBN5=#Pr(h=pA`P$mX-6r>Snv&hP8)(=M$*plf zu42g~%Kb$0K5TlH;fiG$E-9vJNrTEz#qSh#aZvi2_8qhvE2Lw^gM$1Z1h-RA>dX0n zz?c>!7YdWZFwAozl8xo1l_k^;3( zCP(O%#;!oSh01mDL)1%7VChG~rj;7>;=GN|;AF!^&Hywni`-iXqjcI%Eve!Xp^yXDbEMh z>buPgo5iDmS6dDui~Sed2!py5j40uwX?X?Nhw05sFXDk7Ny3?d8Re%^8CsVA%o}W5 zoD69iVm2|&CK7J0Y*8iXsK7>~=dyTnnVfDQ0RHN%21y3X&7nd-lwc+0>OLY3>ClrbXmQ3W;W%Ufr;! z4qX`GT=nkN`TR5nw>8mAo5)PSn^FfiApGsls8w$+*QO_WdTmIwIQ{zvWzttpst;kC-G2wT3ud< zO(t?r?7&)BeYfm@zi&YtnYV$Epl|2Y>fbfLzA4@nP#m{LpQo6h8jMo%PqiIYDj&Li zu3DW`QH-Dt$VEAi_e!Zs0zH~yGaoLFGrzi7z1xk*>eA}sT3We% zVWU8m!cicil`_pc*AAR*SIlZIv;b98!l=`YQZTh)!8dl;h7PTc?bJy-Jf>0BYWcwq|HKFCU6X zev|NJLq%2C*6=}&f zWv!eRydnTEtZmi+cjYdA+-4sqQ?I|!CqENKJ(Uw@Nb)TQwxkQ2>x&Dy!DFn($O!Q5 z&{NcTJ^MeYtSl}qSAZMdgIa-Y_}5MxgMv;fH%{$t+;>`ye9J*Je$_Y=LYpYLHcI}p zsyV{4Ez7En{)aTkHnnKU-%J)C#)?v`q^od}pY3e{G|rL(|UIO5-fll z(wB=Nlnvdj$;3xFJitkbD_7RyL6;JP1n3ZR4^5;Icm_433pO zYu=T)YjemS(_O>43G9wWXf3W+(#7T4+Vw@chH!Izh*ovLmmn)uioKj+sQXvhP|5)d zg-QUnr4siP$@eBD7MI!r4jI|bwf-jgmyYH#4*h_m(3qXT7w&`lR$fAZ`D_-)njo@# zGf#ACFCwNE0vtp)r~HO)=SVBo9st*w%#Ed^475Xvbk3|TuP*p@4c~Z`)`HnIx?zq9 zA&dBlh*V@~jLZ%Xn)K6>&Z<4~)KWsE3|hC?EQq1Fm)k>%GL_9v>AswI<_B1v;CQAQ zd#|SZIlC|LZJBuqU2WfUhhm)9xH*f)uraqPfA9WsYq#E6B|R9rN&JL6IiX}rVNxt*K{3Eg z$+4+mq-FOv8(-PN$}G)4sKo{EM_{cRF>nMY8Q=abiophxS~-(*U?VNFqlbh$|N}RDRHm~Jm!F z^SvjMW{^AQnv$?eV64=#Y49SREg_<9H1Msdg(1SMT~Ey+zA)B~d@vzUqWL@I32%0y zz8Lo4swdP*_?6MW5e!1Dfw?B&fu^Z%WNmCJ#0$Hpkae~NH1UIXkdXqwxe7A zBJD*(Iapg;nOnVkqq0btXhr$RT8%2%yz-n~GtXHxG(AAWS=CF`npsO5Yl{W``dS9H zW($r(J0l!1@?!50n>YVD)|zcY*ZLDUl#6fTa4vwV7@wIf0A=p_+|qIoY&Q4BbYnqg zfR65n0FaR(-%7r&TiTo^WXR9p@MALFw7*eQTm<-MNn5xo^EmAw2r?kL9QKI%h?LC| z@cmqD@fj5Dmw48C?i4#2ECRC(Bre?0DgbSIM`dHRvJ5(~P%hVn5SF;_n_76~*1tt# zQ5=h28GT_P!0_e&T$!ul+cX?TMXDN^<^FTp61#XHXkZY?nORx9x^&G5GPtjyMuYSL z`hipro$F?<00tlit_5531jKx43W((D@S+(&I+G7}Z-`QKGBK&34+IReO4=2RITj_N zXshQQ9Oh(s3^KSm=-ZTbf>?T{BxXwQPZx${pP+w@8uL1OINPN9H-<3`dce|~(c z%Ph-(3FfBBeSr%46Fpl*F(qaJvp`?5e^J@k*2>U=i(rgh8Uk6}eMzlke;hZenK1Nh zlstP?M0W>Xo3LPxraHMy(x0;}=)^O!{JR*JziYAj+VIJS8Tt>jkpLfq#h-xz{}+Ts zq!twBZ1=E9y69XaHfifIzH03b^ie8}IvEJ32v=Z6I+|3QWf~=aHy^BTZNwUENzPBY z4W~Q@q&;O(Fn1Jv)UPT7HCnLQ=7Mj_*FDGaIl;~x(9Fs;K#7wAF(uLAfZJ$k?7QGc z<8(xs)XW`u&z#nF(f4&al%3+8+SyD5hvx4{(SVlCj&)>SGF%3PewaxCu}KsrXloRi zyL2Spo&W2Q90Zwds-v(snr7Gsd88vsZ^+iplnDoxsYiV&)qY^VT41P%Q+w@tWn=Ct z#u|cJd)%Oy(kkRo0IHLVD!bA|o{R$L;)WvPONtU!EXeKKOQVL2W}a)wCI2}Yv_#$y z59Ndaao}% zYA&qF_De-1caa_#p>d=Yw*aE=dZDGm0GImn$Vd(Y#AdmDDJa7AIADqQQe ztHi?C<1H&n99lq=${3E@Nl2cWTcFCKqG1xV77(oCB#=3jgOc7#lJk1qGl#aK^G)0k zzm2?#tPjEg4G#o>NrB9EHZzT#!ZtMO;B!!_jbhqylVX%zlfedqigc@dSl9iqLS%#v zfu%Di+?+Ayks4lUOCLj$;R|ortZqzG=;YVQ_O#hkMyAvlj<*rM=M*EoN?0deuZ6LQ zyH<&I;QKY5xeKR*4PXM~?xPs8vNVM|#BOY|T@r9W+ow1G388-ugT`Bj9d zPD*bvH-wD1{vp3afO_X5Bxl4Yhl3Bt&ZYq_qU@6sh%3;X=FmDj%}q#3NB1U21x)m1 z5n%SvfNyP&v_jo*6stgZlUC6*7?)b3!b`Xe-H>93e5N?A=It~X9 zD6R^rHGPX7g1+!0IX-0+`{}KmzAvk~gSRzTk?o)?j7U$SiSA5m9Z!JfiI+KXMH)Gp z`$PP&L=NIs?h=b_W{hU$$zBGbH6fL4sy2ehGO-`}peiaW7Bnl$fh_#-({OP_6T8~S z!8Bbl6EB=n!Es?~I5+kRnqE8^2BRrz%ApOh*eNgesc}t*o>Q?l;RI-v-O4o`0^`tx zLM=QSvks+4l`;+^GWPOR3j3i55=hJtZ}Y=Bxw>W^KeJAmuE9yGRO;8$8%yD077w8c z#iA@P3(H|{VM~5hAuUak!)Gy*-SuRIP)OFC=gmFv&QL)ey@4>q#dw;Id;N<*bZ!Lu z_}m2;r6CG6>a7PA!l7O|*3IJ3&b{D+A!ibeYlUPjSRX$nQk)_tz9N>jx;F~V-N7ZEuk%dd9#XA&WIbd802G|F zOhP$~0x}XrXcG)r&`KsSwTfeMcqmXxhCy(<;$jT^8au-_K4T`$ZA$+&rr`!jfQL!z6lZ&PHGy`bY2w_Y6fE6j*LDPxg;Qyb+0Eqf~%esM?u54Nnq4J7ha*jvM|N)psE z9YUb!AO{t3XU7#tu)}vb0wOt@&I6H^jmip6AGHWtK7f)4Z?**Q+i{zkGGZ*)&YBD# z0+JvAswH&^K60ln>#g52LN5KmhFfslg zd8K|7jfCXPDqPPZjVxN-V_O zfCcp=;X+29g={=It(0vNw9R0zoQP_LBW&#oO_l%dL8 zDV@y5jz-LYLp}+7Pw}oC2~L_xM}DxO7}4BSC8XV@+@64iW^=o)EdLM6a%E|`%BGDI zE>5whyvG#FAZVs`>YuZr*}%TVI}`xAJ{P?tMCQwxR{4q#bM@N}bPB&D5G4yt5)bG` z3oOa@q!xI{WTCQH*~ryKWyIwtC5p!?ut5JoOOE}rac5F(;(4~1Bec~yF8x~SquLIg^$}UJSrws+zZV; zVHN=<4H@V$`s+B6Ti^&h`}xC&e((bO8G%v)e2GVyo7Y5>OZ50EsCsm#JrUAt`eZ_3 zqq3*SM#4<`^=ZX+Rqs z_fr~SkOm^HnDv0k!jCVBRk|2=cP??`wov$n_;B5jT zB2K$^py(v71#Nd0uahzgge^aZRu~jq#eNuWrkK|Qnu7z^W^^iqgm5#-yk)4L?FEBE!JB$#o%n*(j+om8uS-dC*pS*N6)~6*3rp8e z=FuuWO6Oi32c=ray@Qg{zAOjI#I7sXu15=fVPaq4brwSvYUBipl-&!9*46Obk&-GGJXLtC ztJkVaudPJbZ=zaH@Nfp=1I(a{u-!urIHSotqB!9dWeB&ECtq1x2N+7dk6~7p3-waJ zcO;{2rK0@b{cZe6dy#v&o^kf?tFbM$Jn*n^q>@d!A&oixILx&R0MNoi$} zJwLo2$B~=n;b{6GX9~ZFAkI(X#0j$8Xn;!Z^jML&r|d+^;n{H32xc@gsYPxP-8;q{ z7l$)chFI``XaiJ;7HTCKpe$KS<0!E8gcLP|vt1E(vS~>81;2+JsuA+Sl}9(nUXQz1 z);38phmq>^VHgl@SBvrJH#i(P**;P#|K(x8LmDPFTj6#L?hwf@jnRKyz|W17aT@m_ z@?~vfZe1IO+R1u;b~1~gGeEOrsn5AgJ%dZ&B6Xw0z4R_SSpCg%b}asA(V|sEyh|0$ zi8{w;6I{xgg9r45f>q^uvn;lk%63{x7TMl}g7keV!M)E|oAH#i2^y0&A=MLip>3fn z@22rWy)@ItU+BGQ2d!jv?IkfmFSe*fG#N{Slx`)*wPe!it5#&tKV3K<+Eu$gM zmEZq$H;*s(a*^M4f_DaS$8IbPU08*M62Vkf4JZY3p}h;JVUxc7KmK@G*?+iMI=0h1 zh$mv;&TDkCgsI_DOhQ z`sC|T3XPP37(gpVB33UDxn)zJ!TDM4#Z7d8pq;?vzo_ErN1UU+Hm|HWV%dO3o@{>7 zz>l@^d?QOf2;wr&fXT-dvMfUS%W+rfSB_g1N#v>kDK)1EXnO*Y{WdU(a7uY%X$T<` zAG_PK=?@fXRhS!PhAj{Rs%fMS=eGaIKn40Te?u_96+gCJ8fc}3?KXjMXQ~cc?!cAg zttgtis6EAuS*%m^ZIHEdUL!9KMzTP?>&{M4pT(?NN;a80TR2771T6){6Mjx9d*QbX zNSl)?tOP#1R@W;cAF~(=p5d62|4gm9R&AB!_X39zzaf4SkUfCIv7Gb@4bv414oR11 z|5O&SScm!F;hp8Ex6p5*h;IXXouup1?`#`W0ufF1X1&PJKo^w$Zv(M(VvkEV;98g- zlYSw1j`xwCi${^y9Y3oCE?PO_&fx+d}@cysjSmQ9PF=DrM)fR|=51-W+fp(@a}|Nd9c& z4-4vQ&MBSR{B+*vOR&ugr+(le8S$IP!u04DljO*M2f|j!?G?aSW-<@#3I)w{d@euR zP9kH7WtP8;Qe)1IXG4n|nSE9vGR?f=jP%n`cI@`FIe{3hvuZp2HXpm#91j3bvhK2r zuPk}wzegbO2m~I1z~4Rytlx3dqi%ilBS2~r?OOoe5E=isZls=S0=}(^i08rBL(+_VTor2OEyMJ>3+kw(I1Eon^YCr32 zDEV>P4{tD?#HHVV@^#D++{vCwU|9Sf@A9^Ry+d$4e z2VD9Ps&>cP5nlcM%@u zU^+>0f4T9d&U2u+AHZOlv`~KjwlkrW|Hi|^8Biyo)IYlGZEr6i-PCm)ld9D?M3H4 zb|0EI?`}$NkuxOx_ntTXgV#JJnIaO5Yk9MFbMolV{NdZS$45^{Zu-1Gz<*%>tlx1n VXqY5V<$rH}T9SO_Yi{A`{{c7kV#NRe literal 0 HcmV?d00001 diff --git a/build/bootstrap/mkdeps.com b/build/bootstrap/mkdeps.com index 7a8296c2d66fc45b8123d60a4f48dee53faf88d6..bbb1e066d30fe2c3b55a32ee030d4ac9de2d33cb 100755 GIT binary patch delta 39399 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 literal 77824 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 OBT$b(Jpz9q5%@okI!{~x diff --git a/build/bootstrap/mkdir.com b/build/bootstrap/mkdir.com new file mode 100755 index 0000000000000000000000000000000000000000..2307febea1f2a48f57d010e79bd1679c49d259a8 GIT binary patch literal 49152 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