diff --git a/Makefile b/Makefile index 2c29072ff..44d406136 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,7 @@ include third_party/stb/stb.mk # │ include dsp/scale/scale.mk # │ include dsp/mpeg/mpeg.mk # │ include dsp/dsp.mk # │ +include third_party/zlib/gz/gz.mk # │ include third_party/musl/musl.mk # │ include third_party/getopt/getopt.mk # │ include libc/libc.mk #─┘ @@ -140,6 +141,8 @@ include third_party/regex/regex.mk #─┘ include third_party/third_party.mk include libc/testlib/testlib.mk include tool/viz/lib/vizlib.mk +include tool/args/args.mk +include test/tool/args/test.mk include third_party/linenoise/linenoise.mk include third_party/maxmind/maxmind.mk include third_party/lua/lua.mk @@ -333,6 +336,7 @@ COSMOPOLITAN_HEADERS = \ THIRD_PARTY_GETOPT \ THIRD_PARTY_MUSL \ THIRD_PARTY_ZLIB \ + THIRD_PARTY_ZLIB_GZ \ THIRD_PARTY_REGEX o/$(MODE)/cosmopolitan.a: \ diff --git a/examples/check.c b/examples/check.c new file mode 100644 index 000000000..293ab01e5 --- /dev/null +++ b/examples/check.c @@ -0,0 +1,88 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/log/check.h" + +/** + * @fileoverview Check Macros + * + * The simplest assertion is: + * + * assert(123 == x); + * + * This has some downsides: + * + * 1. It's nice to know what `x` is when it crashes + * 2. It's sometimes nice to have the check always take effect. + * 3. It's problematic that assert() can't do __builtin_assume() + * + * Cosmopolitan provides alternative macros like: + * + * - `CHECK(EXPR, ...)` + * - `CHECK_EQ(WANT, GOT, ...)` + * - `CHECK_NE(WANT, GOT, ...)` + * - `CHECK_GT(WANT, GOT, ...)` + * - `CHECK_LT(WANT, GOT, ...)` + * - `CHECK_NOTNULL(EXPR, ...)` + * + * The CHECK macros always happen. They always kill the process when + * they fail. Printf formatting information may be provided as extra + * arguments. On the other hand, there exists: + * + * - `DCHECK(EXPR, ...)` + * - `DCHECK_EQ(WANT, GOT, ...)` + * - `DCHECK_NE(WANT, GOT, ...)` + * - `DCHECK_GT(WANT, GOT, ...)` + * - `DCHECK_LT(WANT, GOT, ...)` + * - `DCHECK_NOTNULL(EXPR, ...)` + * + * The DCHECK macros always happen when NDEBUG isn't defined. When + * NDEBUG is defined, they still happen, but in a special way that + * causes the compiler to recognize their failure as undefined behavior. + * What this means is that, if the provided expressions are pure without + * side-effects, then the code compiles down to nothing and the compiler + * may be able to use the knowledge of something being the case in order + * to optimize other code adjacent to your DCHECK. + * + * In the default build modes, this prints lots of information: + * + * error:examples/check.c:23:check.com: check failed on nightmare pid 15412 + * CHECK_EQ(123, some_source_code); + * → 0x7b (123) + * == 0x64 (some_source_code) + * extra info: hello + * EUNKNOWN/0/No error information + * ./o//examples/check.com + * 0x0000000000407404: __die at libc/log/die.c:42 + * 0x0000000000407340: __check_fail at libc/log/checkfail.c:73 + * 0x00000000004071d0: main at examples/check.c:23 + * 0x000000000040256e: cosmo at libc/runtime/cosmo.S:69 + * 0x000000000040217d: _start at libc/crt/crt.S:85 + * + * In NDEBUG mode (e.g. MODE=rel, MODE=tiny, etc.) this prints a much + * simpler message that, most importantly, doesn't include any source + * code, although it still includes the file name for reference. + * + * error:examples/check.c:14: check failed: 123 == 100: extra info: hello + * + * That way your release binaries won't leak CI. You may optionally link + * the following functions to further expand the information shown by + * the NDEBUG check failure: + * + * STATIC_YOINK("__die"); + * STATIC_YOINK("strerror"); + * + * Please note that backtraces aren't ever available in MODE=tiny. + */ + +int main(int argc, char *argv[]) { + int some_source_code = 100; + CHECK_EQ(123, some_source_code, "extra info: %s", "hello"); + return 0; +} diff --git a/examples/crashreport.c b/examples/crashreport.c index bef370ac6..03f0650ff 100644 --- a/examples/crashreport.c +++ b/examples/crashreport.c @@ -22,7 +22,7 @@ * o//examples/crashreport.com */ -int main(int argc, char *argv[]) { +noubsan int main(int argc, char *argv[]) { volatile int64_t x; ShowCrashReports(); return 1 / (x = 0); diff --git a/examples/exit.c b/examples/exit.c new file mode 100644 index 000000000..24cd4e9ae --- /dev/null +++ b/examples/exit.c @@ -0,0 +1,14 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/fmt/conv.h" + +int main(int argc, char *argv[]) { + return atoi(argc > 1 ? argv[1] : "0"); +} diff --git a/examples/rlimit.c b/examples/rlimit.c index 47325062c..95cdcbda5 100644 --- a/examples/rlimit.c +++ b/examples/rlimit.c @@ -11,6 +11,7 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/rlimit.h" #include "libc/errno.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/log/color.internal.h" #include "libc/macros.internal.h" @@ -37,12 +38,12 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) { lim.rlim_cur = MIN(soft, lim.rlim_max); if (!setrlimit(resource, &lim)) { fprintf(stderr, "%sNOTE: SETRLIMIT(%s) DOWNGRADED TO {%,ld, %,ld}\n", - __strace_rlimit_name(resource), lim.rlim_cur, lim.rlim_max); + DescribeRlimitName(resource), lim.rlim_cur, lim.rlim_max); return; } } fprintf(stderr, "ERROR: SETRLIMIT(%s, %,ld, %,ld) FAILED %m%n", - __strace_rlimit_name(resource), soft, hard); + DescribeRlimitName(resource), soft, hard); exit(1); } } @@ -63,9 +64,8 @@ int main(int argc, char *argv[]) { for (i = 0; i < RLIM_NLIMITS; ++i) { rc = getrlimit(i, &rlim); - printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n", - __strace_rlimit_name(i), rlim.rlim_cur, rlim.rlim_max, rc, - !rc ? "" : strerror(errno)); + printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n", DescribeRlimitName(i), + rlim.rlim_cur, rlim.rlim_max, rc, !rc ? "" : strerror(errno)); } return 0; diff --git a/examples/shell.c b/examples/shell.c new file mode 100644 index 000000000..117ea81e7 --- /dev/null +++ b/examples/shell.c @@ -0,0 +1,172 @@ +#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/sigbits.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/log/internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/dt.h" +#include "libc/sysv/consts/sig.h" +#include "libc/x/x.h" +#include "third_party/linenoise/linenoise.h" + +/** + * @fileoverview Shell that works on Windows. + * + * 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. + * + * One day we'll have UNBOURNE.COM working on Windows but the code isn't + * very maintainable sadly. + */ + +static void AddUniqueCompletion(linenoiseCompletions *c, char *s) { + size_t i; + if (!s) return; + for (i = 0; i < c->len; ++i) { + if (!strcmp(s, c->cvec[i])) { + return; + } + } + c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec)); + c->cvec[c->len - 1] = s; +} + +static void CompleteFilename(const char *p, const char *q, const char *b, + linenoiseCompletions *c) { + DIR *d; + char *buf; + const char *g; + struct dirent *e; + if ((buf = malloc(512))) { + if ((g = memrchr(p, '/', q - p))) { + *(char *)mempcpy(buf, p, MIN(g - p, 511)) = 0; + p = ++g; + } else { + strcpy(buf, "."); + } + if ((d = opendir(buf))) { + while ((e = readdir(d))) { + if (!strcmp(e->d_name, ".")) continue; + if (!strcmp(e->d_name, "..")) continue; + if (!strncmp(e->d_name, p, q - p)) { + snprintf(buf, 512, "%.*s%s%s", p - b, b, e->d_name, + e->d_type == DT_DIR ? "/" : ""); + AddUniqueCompletion(c, strdup(buf)); + } + } + closedir(d); + } + free(buf); + } +} + +static void ShellCompletion(const char *p, linenoiseCompletions *c) { + bool slashed; + const char *q, *b; + for (slashed = false, b = p, q = (p += strlen(p)); p > b; --p) { + if (p[-1] == '/' && p[-1] == '\\') slashed = true; + if (!isalnum(p[-1]) && + (p[-1] != '.' && p[-1] != '_' && p[-1] != '-' && p[-1] != '+' && + p[-1] != '[' && p[-1] != '/' && p[-1] != '\\')) { + break; + } + } + CompleteFilename(p, q, b, c); +} + +static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) { + char *h = 0; + linenoiseCompletions c = {0}; + ShellCompletion(p, &c); + if (c.len == 1) { + h = strdup(c.cvec[0] + strlen(p)); + } + linenoiseFreeCompletions(&c); + return h; +} + +int main(int argc, char *argv[]) { + int n, ws, pid; + char *prog, path[PATH_MAX]; + sigset_t chldmask, savemask; + struct sigaction ignore, saveint, savequit; + char *p, *line, **args, *arg, *start, *state, prompt[64]; + linenoiseSetFreeHintsCallback(free); + linenoiseSetHintsCallback(ShellHint); + linenoiseSetCompletionCallback(ShellCompletion); + stpcpy(prompt, "$ "); + while ((line = linenoiseWithHistory(prompt, "cmd"))) { + n = 0; + start = line; + 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; + } + 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); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); + + if (!fork()) { + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + execv(prog, args); + _Exit(127); + } + + wait(&ws); + 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 (!__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, "$ "); + + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + } else { + fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]); + } + } + free(line); + free(args); + } +} diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index c5469a5e5..c5cf3aadd 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -68,6 +68,10 @@ $(LIBC_CALLS_A).pkg: \ 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/getcwd.greg.o \ +o/$(MODE)/libc/calls/getcwd-xnu.greg.o \ +o/$(MODE)/libc/calls/getprogramexecutablename.greg.o \ o/$(MODE)/libc/calls/raise.o: \ OVERRIDE_COPTS += \ -ffreestanding \ @@ -116,7 +120,6 @@ 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/describeopenflags.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 f091d6719..f7deab6e6 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -23,6 +23,7 @@ #include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" @@ -70,7 +71,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { } if (!__time_critical) { STRACE("clock_gettime(%d, [%s]) → %d% m", clockid, - __strace_timespec(buf, sizeof(buf), rc, ts), rc); + DescribeTimespec(buf, sizeof(buf), rc, ts), rc); } return rc; } diff --git a/libc/calls/strace_sigaction.greg.c b/libc/calls/describesigaction.greg.c similarity index 76% rename from libc/calls/strace_sigaction.greg.c rename to libc/calls/describesigaction.greg.c index e34df149b..c6be76e17 100644 --- a/libc/calls/strace_sigaction.greg.c +++ b/libc/calls/describesigaction.greg.c @@ -16,16 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" -privileged const char *__strace_sigaction(char *buf, size_t bufsize, int rc, - const struct sigaction *sa) { - char maskbuf[41]; +const char *DescribeSigaction(char *buf, size_t bufsize, int rc, + const struct sigaction *sa) { + char maskbuf[64]; if (rc == -1) return "n/a"; if (!sa) return "NULL"; - ksnprintf(buf, bufsize, "{.sa_handler=%p, .sa_flags=%#lx, .sa_mask=%s}", - sa->sa_handler, sa->sa_flags, - __strace_sigset(maskbuf, sizeof(maskbuf), rc, &sa->sa_mask)); + if ((!IsAsan() && kisdangerous(sa)) || + (IsAsan() && !__asan_is_valid(sa, sizeof(*sa)))) { + ksnprintf(buf, sizeof(buf), "%p", sa); + } else { + ksnprintf(buf, bufsize, "{.sa_handler=%p, .sa_flags=%#lx, .sa_mask=%s}", + sa->sa_handler, sa->sa_flags, + DescribeSigset(maskbuf, sizeof(maskbuf), rc, &sa->sa_mask)); + } return buf; } diff --git a/libc/calls/describesigset.greg.c b/libc/calls/describesigset.greg.c new file mode 100644 index 000000000..e72078e1f --- /dev/null +++ b/libc/calls/describesigset.greg.c @@ -0,0 +1,64 @@ +/*-*- 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/bits/popcnt.h" +#include "libc/calls/sigbits.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/str/str.h" + +const char *DescribeSigset(char *buf, size_t bufsize, int rc, + const sigset_t *ss) { + bool gotsome; + int i, n, sig; + sigset_t sigset; + + 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); + return buf; + } + + i = 0; + n = bufsize; + sigset = *ss; + gotsome = false; + if (popcnt(sigset.__bits[0]) + popcnt(sigset.__bits[1]) > 64) { + i += ksnprintf(buf + i, n - i, "~"); + sigset.__bits[0] = ~sigset.__bits[0]; + sigset.__bits[1] = ~sigset.__bits[1]; + } + i += ksnprintf(buf + i, n - i, "{"); + for (sig = 1; sig < 128; ++sig) { + if (sigismember(&sigset, sig)) { + if (gotsome) { + sig += ksnprintf(buf + sig, n - sig, ", "); + } else { + gotsome = true; + } + sig += ksnprintf(buf + sig, n - sig, "%s", strsignal(sig)); + } + } + i += ksnprintf(buf + i, n - i, "}"); + + return buf; +} diff --git a/libc/calls/directmap-nt.c b/libc/calls/directmap-nt.c index 78e1f58ee..e8f015ede 100644 --- a/libc/calls/directmap-nt.c +++ b/libc/calls/directmap-nt.c @@ -48,7 +48,7 @@ textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot, handle = kNtInvalidHandleValue; } - if (flags & MAP_PRIVATE) { + if ((flags & MAP_TYPE) != MAP_SHARED) { sec = 0; // MAP_PRIVATE isn't inherited across fork() } else { sec = &kNtIsInheritable; // MAP_SHARED gives us zero-copy fork() @@ -60,14 +60,13 @@ textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot, // note that open-nt.c always requests an kNtGenericExecute accessmask iscow = false; if (handle != -1) { - if (flags & MAP_PRIVATE) { + if ((flags & MAP_TYPE) != MAP_SHARED) { // windows has cow pages but they can't propagate across fork() // that means we only get copy-on-write for the root process :( fl = (struct ProtectNt){kNtPageExecuteWritecopy, kNtFileMapCopy | kNtFileMapExecute}; iscow = true; } else { - assert(flags & MAP_SHARED); if ((g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY) { fl = (struct ProtectNt){kNtPageExecuteRead, kNtFileMapRead | kNtFileMapExecute}; diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index cadba32f0..2e602e121 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.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" @@ -50,7 +51,7 @@ int faccessat(int dirfd, const char *path, int mode, uint32_t flags) { } else { rc = sys_faccessat_nt(dirfd, path, mode, flags); } - STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", __strace_dirfd(buf, dirfd), + STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", DescribeDirfd(buf, dirfd), path, mode, flags, rc); return rc; } diff --git a/libc/calls/fchmodat.c b/libc/calls/fchmodat.c index 26aa9305e..f8b617442 100644 --- a/libc/calls/fchmodat.c +++ b/libc/calls/fchmodat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -51,7 +52,7 @@ int fchmodat(int dirfd, const char *path, uint32_t mode, int flags) { } else { rc = sys_fchmodat_nt(dirfd, path, mode, flags); } - STRACE("fchmodat(%s, %#s, %#o, %d) → %d% m", __strace_dirfd(buf, dirfd), path, + STRACE("fchmodat(%s, %#s, %#o, %d) → %d% m", DescribeDirfd(buf, dirfd), path, mode, flags, rc); return rc; } diff --git a/libc/calls/fchownat.c b/libc/calls/fchownat.c index fc1e5530c..cec5ab92b 100644 --- a/libc/calls/fchownat.c +++ b/libc/calls/fchownat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -49,7 +50,7 @@ int fchownat(int dirfd, const char *path, uint32_t uid, uint32_t gid, } else { rc = sys_fchownat(dirfd, path, uid, gid, flags); } - STRACE("fchownat(%s, %#s, %d, %d, %#b) → %d% m", __strace_dirfd(sb, dirfd), + STRACE("fchownat(%s, %#s, %d, %d, %#b) → %d% m", DescribeDirfd(sb, dirfd), path, uid, gid, flags, rc); return rc; } diff --git a/libc/calls/fstat.c b/libc/calls/fstat.c index b7d7d46a3..564a25e1a 100644 --- a/libc/calls/fstat.c +++ b/libc/calls/fstat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -45,6 +46,6 @@ int fstat(int fd, struct stat *st) { } else { rc = sys_fstat_nt(__getfdhandleactual(fd), st); } - STRACE("fstat(%d, [%s]) → %d% m", fd, __strace_stat(rc, st), rc); + STRACE("fstat(%d, [%s]) → %d% m", fd, DescribeStat(rc, st), rc); return rc; } diff --git a/libc/calls/fstatat.c b/libc/calls/fstatat.c index 008c0908f..0a13947a1 100644 --- a/libc/calls/fstatat.c +++ b/libc/calls/fstatat.c @@ -24,6 +24,7 @@ #include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/str/str.h" @@ -70,7 +71,7 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) { } else { rc = sys_fstatat_nt(dirfd, path, st, flags); } - STRACE("fstatat(%s, %#s, [%s], %s) → %d% m", __strace_dirfd(buf, dirfd), path, - __strace_stat(rc, st), __strace_fstatat_flags(flags), rc); + STRACE("fstatat(%s, %#s, [%s], %s) → %d% m", DescribeDirfd(buf, dirfd), path, + DescribeStat(rc, st), __strace_fstatat_flags(flags), rc); return rc; } diff --git a/libc/calls/getcwd-xnu.c b/libc/calls/getcwd-xnu.greg.c similarity index 100% rename from libc/calls/getcwd-xnu.c rename to libc/calls/getcwd-xnu.greg.c diff --git a/libc/calls/getcwd.c b/libc/calls/getcwd.greg.c similarity index 100% rename from libc/calls/getcwd.c rename to libc/calls/getcwd.greg.c diff --git a/libc/calls/getexecutablename.c b/libc/calls/getprogramexecutablename.greg.c similarity index 99% rename from libc/calls/getexecutablename.c rename to libc/calls/getprogramexecutablename.greg.c index 455942bb7..f3ecbab09 100644 --- a/libc/calls/getexecutablename.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -112,6 +112,7 @@ char *GetProgramExecutableName(void) { program_executable_name, program_executable_name + sizeof(program_executable_name)); errno = e; + once = true; } return program_executable_name; } diff --git a/libc/calls/getrlimit.c b/libc/calls/getrlimit.c index 25bacf842..cb0c2ee61 100644 --- a/libc/calls/getrlimit.c +++ b/libc/calls/getrlimit.c @@ -21,6 +21,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/errfuns.h" @@ -48,7 +49,7 @@ int getrlimit(int resource, struct rlimit *rlim) { } else { rc = einval(); } - STRACE("getrlimit(%s, [%s]) → %d% m", __strace_rlimit_name(resource), - __strace_rlimit(buf, sizeof(buf), rc, rlim), rc); + STRACE("getrlimit(%s, [%s]) → %d% m", DescribeRlimitName(resource), + DescribeRlimit(buf, sizeof(buf), rc, rlim), rc); return rc; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 50c0e3a85..286b1cd1a 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -229,6 +229,7 @@ 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; diff --git a/libc/calls/linkat.c b/libc/calls/linkat.c index 816b31a59..7b6b86888 100644 --- a/libc/calls/linkat.c +++ b/libc/calls/linkat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -52,7 +53,7 @@ int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, rc = sys_linkat_nt(olddirfd, oldpath, newdirfd, newpath); } STRACE("linkat(%s, %#s, %s, %#s, %#b) → %d% m", - __strace_dirfd(buf[0], olddirfd), oldpath, - __strace_dirfd(buf[1], newdirfd), newpath, flags, rc); + DescribeDirfd(buf[0], olddirfd), oldpath, + DescribeDirfd(buf[1], newdirfd), newpath, flags, rc); return rc; } diff --git a/libc/calls/mkdirat.c b/libc/calls/mkdirat.c index bdb0b5a38..67c5536fc 100644 --- a/libc/calls/mkdirat.c +++ b/libc/calls/mkdirat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.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" @@ -50,7 +51,7 @@ int mkdirat(int dirfd, const char *path, unsigned mode) { } else { rc = sys_mkdirat_nt(dirfd, path, mode); } - STRACE("mkdirat(%s, %#s, %#o) → %d% m", __strace_dirfd(buf, dirfd), path, - mode, rc); + STRACE("mkdirat(%s, %#s, %#o) → %d% m", DescribeDirfd(buf, dirfd), path, mode, + rc); return rc; } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index 3cde3bf05..cffd10ddd 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -20,6 +20,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" /** @@ -45,8 +46,8 @@ noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) { } if (!__time_critical) { STRACE("nanosleep(%s, [%s]) → %d% m", - __strace_timespec(buf[0], sizeof(buf[0]), rc, req), - __strace_timespec(buf[1], sizeof(buf[1]), rc, rem), rc); + DescribeTimespec(buf[0], sizeof(buf[0]), rc, req), + DescribeTimespec(buf[1], sizeof(buf[1]), rc, rem), rc); } return rc; } diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 7f07dd379..19968a080 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -23,6 +23,7 @@ #include "libc/dce.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/log/log.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" @@ -75,7 +76,7 @@ int openat(int dirfd, const char *file, int flags, ...) { } else { rc = efault(); } - STRACE("openat(%s, %#s, %s, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, + STRACE("openat(%s, %#s, %s, %#o) → %d% m", DescribeDirfd(buf, dirfd), file, DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, rc); return rc; diff --git a/libc/calls/poll.c b/libc/calls/poll.c index f58228e32..6fb4d8ae0 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -84,20 +84,22 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) { #if defined(SYSDEBUG) && _POLLTRACE if (__strace > 0) { - if (rc == -1 && errno == EFAULT) { - STRACE("poll(%p, %'lu, %'d) → %d% lm", fds, nfds, timeout_ms, rc); + kprintf(STRACE_PROLOGUE "poll("); + if ((!IsAsan() && kisdangerous(fds)) || + (IsAsan() && !__asan_is_valid(fds, nfds * sizeof(struct pollfd)))) { + kprintf("%p", fds); } else { char flagbuf[2][64]; - kprintf(STRACE_PROLOGUE "poll({"); + kprintf("[{"); for (i = 0; i < MIN(5, nfds); ++i) { kprintf( "%s{%d, %s, %s}", i ? ", " : "", fds[i].fd, DescribePollFlags(flagbuf[0], sizeof(flagbuf[0]), fds[i].events), DescribePollFlags(flagbuf[1], sizeof(flagbuf[1]), fds[i].revents)); } - kprintf("%s}, %'zu, %'d) → %d% lm\n", i == 5 ? "..." : "", nfds, - timeout_ms, rc); + kprintf("%s}]", i == 5 ? "..." : ""); } + kprintf(", %'zu, %'d) → %d% lm\n", nfds, timeout_ms, rc); } #endif diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 94f83a1b8..4b527d88c 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -25,6 +25,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" @@ -113,13 +114,9 @@ ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { rc = Preadv(fd, iov, iovlen, off); #if defined(SYSDEBUG) && _DATATRACE if (__strace > 0) { - if (rc == -1 && errno == EFAULT) { - STRACE("preadv(%d, %p, %d, %'ld) → %'zd% m", fd, iov, iovlen, off, rc); - } else { - kprintf(STRACE_PROLOGUE "preadv(%d, [", fd); - __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf("], %d, %'ld) → %'ld% m\n", iovlen, off, rc); - } + kprintf(STRACE_PROLOGUE "preadv(%d, [", fd); + DescribeIov(iov, iovlen, rc != -1 ? rc : 0); + kprintf("], %d, %'ld) → %'ld% m\n", iovlen, off, rc); } #endif return rc; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index a480338bc..3d8744415 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -24,6 +24,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" @@ -118,13 +119,9 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { rc = Pwritev(fd, iov, iovlen, off); #if defined(SYSDEBUG) && _DATATRACE if (__strace > 0) { - if (rc == -1 && errno == EFAULT) { - STRACE("pwritev(%d, %p, %d, %'ld) → %'zd% m", fd, iov, iovlen, off, rc); - } else { - kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd); - __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d, %'ld) → %'ld% m\n", iovlen, off, rc); - } + kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd); + DescribeIov(iov, iovlen, rc != -1 ? rc : 0); + kprintf(", %d, %'ld) → %'ld% m\n", iovlen, off, rc); } #endif return rc; diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index 58da8789e..d35116378 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -21,6 +21,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -53,7 +54,7 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { } else { bytes = sys_readlinkat_nt(dirfd, path, buf, bufsiz); } - STRACE("readlinkat(%s, %#s, [%#.*s]) → %d% m", __strace_dirfd(sb, dirfd), - path, MAX(0, bytes), buf, bytes); + STRACE("readlinkat(%s, %#s, [%#.*s]) → %d% m", DescribeDirfd(sb, dirfd), path, + MAX(0, bytes), buf, bytes); return bytes; } diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 490f02c1c..54ae7ee12 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" @@ -71,7 +72,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { STRACE("readv(%d, %p, %d) → %'zd% m", fd, iov, iovlen, rc); } else { kprintf(STRACE_PROLOGUE "readv(%d, [", fd); - __strace_iov(iov, iovlen, rc != -1 ? rc : 0); + DescribeIov(iov, iovlen, rc != -1 ? rc : 0); kprintf("], %d) → %'ld% m\n", iovlen, rc); } } diff --git a/libc/calls/renameat.c b/libc/calls/renameat.c index 71bde23d6..107ef3b69 100644 --- a/libc/calls/renameat.c +++ b/libc/calls/renameat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.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" @@ -57,7 +58,7 @@ int renameat(int olddirfd, const char *oldpath, int newdirfd, } else { rc = sys_renameat_nt(olddirfd, oldpath, newdirfd, newpath); } - STRACE("renameat(%s, %#s, %s, %#s) → %d% m", __strace_dirfd(buf[0], olddirfd), - oldpath, __strace_dirfd(buf[1], newdirfd), newpath, rc); + STRACE("renameat(%s, %#s, %s, %#s) → %d% m", DescribeDirfd(buf[0], olddirfd), + oldpath, DescribeDirfd(buf[1], newdirfd), newpath, rc); return rc; } diff --git a/libc/calls/sched_getaffinity.c b/libc/calls/sched_getaffinity.c new file mode 100644 index 000000000..023b217c3 --- /dev/null +++ b/libc/calls/sched_getaffinity.c @@ -0,0 +1,45 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/str/str.h" + +/** + * Gets kernel scheduling for particular CPUs. + * + * @param pid is the process or thread id (or 0 for caller) + * @param size is byte length of bitset + * @param bitset receives bitset and should be uint64_t[16] in order to + * work on older versions of Linux + * @return 0 on success, or -1 w/ errno + * @raise ENOSYS on non-Linux + */ +int sched_getaffinity(int tid, size_t size, void *bitset) { + long rc; + rc = sys_sched_getaffinity(tid, size, bitset); + if (rc != -1) { + if (rc < size) { + memset((char *)bitset + rc, 0, size - rc); + } + rc = 0; + } + STRACE("sched_getaffinity(%d, %'zu, %p) → %d% m", tid, size, bitset); + return rc; +} diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index f1c8df0cd..99d6db694 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -19,6 +19,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/nt/enum/processaccess.h" @@ -63,14 +64,18 @@ static textwindows dontinline int sys_sched_setaffinity_nt(int pid, /** * Asks kernel to only schedule process on particular CPUs. * - * @param pid is the process or thread id (or 0 for caller) + * @param tid is the process or thread id (or 0 for caller) * @param bitsetsize is byte length of bitset * @return 0 on success, or -1 w/ errno + * @raise ENOSYS if not Linux or Windows */ -int sched_setaffinity(int pid, uint64_t bitsetsize, const void *bitset) { +int sched_setaffinity(int tid, uint64_t bitsetsize, const void *bitset) { + int rc; if (!IsWindows()) { - return sys_sched_setaffinity(pid, bitsetsize, bitset); + rc = sys_sched_setaffinity(tid, bitsetsize, bitset); } else { - return sys_sched_setaffinity_nt(pid, bitsetsize, bitset); + rc = sys_sched_setaffinity_nt(tid, bitsetsize, bitset); } + STRACE("sched_setaffinity(%d, %'zu, %p) → %d% m", tid, bitsetsize, bitset); + return rc; } diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c index 5ef298940..6cd4a3412 100644 --- a/libc/calls/setrlimit.c +++ b/libc/calls/setrlimit.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/errfuns.h" @@ -82,7 +83,7 @@ int setrlimit(int resource, const struct rlimit *rlim) { } else { rc = einval(); } - STRACE("setrlimit(%s, %s) → %d% m", __strace_rlimit_name(resource), - __strace_rlimit(buf, sizeof(buf), 0, rlim), rc); + STRACE("setrlimit(%s, %s) → %d% m", DescribeRlimitName(resource), + DescribeRlimit(buf, sizeof(buf), 0, rlim), rc); return rc; } diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 17ac1962d..eb36e030d 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -33,6 +33,7 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" @@ -449,7 +450,7 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { rc = __sigaction(sig, act, oldact); } STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, - __strace_sigaction(buf[0], sizeof(buf[0]), 0, act), - __strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc); + DescribeSigaction(buf[0], sizeof(buf[0]), 0, act), + DescribeSigaction(buf[1], sizeof(buf[1]), rc, oldact), rc); return rc; } diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index f34fef059..26bea2137 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -26,6 +26,7 @@ #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/str/str.h" @@ -78,7 +79,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) { *opt_out_oldset = old; } STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how), - __strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set), - __strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc); + DescribeSigset(buf[0], sizeof(buf[0]), 0, opt_set), + DescribeSigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc); return rc; } diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c index 098327c28..acd2ae612 100644 --- a/libc/calls/sigsuspend.c +++ b/libc/calls/sigsuspend.c @@ -25,6 +25,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/log/backtrace.internal.h" #include "libc/nt/errors.h" #include "libc/nt/synchronization.h" @@ -47,7 +48,7 @@ int sigsuspend(const sigset_t *ignore) { char buf[41]; long ms, totoms; sigset_t save, mask, *arg; - STRACE("sigsuspend(%s) → ...", __strace_sigset(buf, sizeof(buf), 0, ignore)); + STRACE("sigsuspend(%s) → ...", DescribeSigset(buf, sizeof(buf), 0, ignore)); if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) { rc = efault(); } else if (IsXnu() || IsOpenbsd()) { diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 509bfcd8f..1051c0044 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -53,14 +53,6 @@ COSMOPOLITAN_C_START_ extern int __strace; void __stracef(const char *, ...); -void __strace_iov(const struct iovec *, int, ssize_t); -const char *__strace_stat(int, const struct stat *); -const char *__strace_sigaction(char *, size_t, int, const struct sigaction *); -const char *__strace_sigset(char[41], size_t, int, const sigset_t *); -const char *__strace_rlimit_name(int); -const char *__strace_rlimit(char[41], size_t, int, const struct rlimit *); -const char *__strace_timespec(char[45], size_t, int, const struct timespec *); -const char *__strace_dirfd(char[12], int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/strace_rlimit.c b/libc/calls/strace_rlimit.c deleted file mode 100644 index b05f60f77..000000000 --- a/libc/calls/strace_rlimit.c +++ /dev/null @@ -1,58 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" -#include "libc/calls/struct/rlimit.h" -#include "libc/fmt/itoa.h" -#include "libc/intrin/kprintf.h" -#include "libc/sysv/consts/rlimit.h" - -const char *__strace_rlimit_name(int resource) { - static char buf[12]; - if (resource != 127) { - if (resource == RLIMIT_AS) return "RLIMIT_AS"; - if (resource == RLIMIT_CPU) return "RLIMIT_CPU"; - if (resource == RLIMIT_FSIZE) return "RLIMIT_FSIZE"; - if (resource == RLIMIT_NPROC) return "RLIMIT_NPROC"; - if (resource == RLIMIT_NOFILE) return "RLIMIT_NOFILE"; - if (resource == RLIMIT_RSS) return "RLIMIT_RSS"; - if (resource == RLIMIT_DATA) return "RLIMIT_DATA"; - if (resource == RLIMIT_CORE) return "RLIMIT_CORE"; - if (resource == RLIMIT_STACK) return "RLIMIT_STACK"; - if (resource == RLIMIT_SIGPENDING) return "RLIMIT_SIGPENDING"; - if (resource == RLIMIT_MEMLOCK) return "RLIMIT_MEMLOCK"; - if (resource == RLIMIT_LOCKS) return "RLIMIT_LOCKS"; - if (resource == RLIMIT_MSGQUEUE) return "RLIMIT_MSGQUEUE"; - if (resource == RLIMIT_NICE) return "RLIMIT_NICE"; - if (resource == RLIMIT_RTPRIO) return "RLIMIT_RTPRIO"; - if (resource == RLIMIT_RTTIME) return "RLIMIT_RTTIME"; - if (resource == RLIMIT_SWAP) return "RLIMIT_SWAP"; - if (resource == RLIMIT_SBSIZE) return "RLIMIT_SBSIZE"; - if (resource == RLIMIT_NPTS) return "RLIMIT_NPTS"; - } - FormatInt32(buf, resource); - return buf; -} - -privileged const char *__strace_rlimit(char buf[64], size_t bufsize, int rc, - const struct rlimit *rlim) { - if (rc == -1) return "n/a"; - if (!rlim) return "NULL"; - ksnprintf(buf, bufsize, "{%'ld, %'ld}", rlim->rlim_cur, rlim->rlim_max); - return buf; -} diff --git a/libc/calls/symlinkat.c b/libc/calls/symlinkat.c index 26242b377..de4540654 100644 --- a/libc/calls/symlinkat.c +++ b/libc/calls/symlinkat.c @@ -21,6 +21,7 @@ #include "libc/calls/strace.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" @@ -50,6 +51,6 @@ int symlinkat(const char *target, int newdirfd, const char *linkpath) { rc = sys_symlinkat_nt(target, newdirfd, linkpath); } STRACE("symlinkat(%#s, %s, %#s) → %d% m", target, - __strace_dirfd(buf, newdirfd), linkpath); + DescribeDirfd(buf, newdirfd), linkpath); return rc; } diff --git a/libc/calls/unlinkat.c b/libc/calls/unlinkat.c index 5326350ce..edc626d35 100644 --- a/libc/calls/unlinkat.c +++ b/libc/calls/unlinkat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.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" @@ -49,7 +50,7 @@ int unlinkat(int dirfd, const char *path, int flags) { } else { rc = sys_unlinkat_nt(dirfd, path, flags); } - STRACE("unlinkat(%s, %#s, %#b) → %d% m", __strace_dirfd(buf, dirfd), path, + STRACE("unlinkat(%s, %#s, %#b) → %d% m", DescribeDirfd(buf, dirfd), path, flags, rc); return rc; } diff --git a/libc/calls/utimensat.c b/libc/calls/utimensat.c index 8861f0420..d4efa6aa2 100644 --- a/libc/calls/utimensat.c +++ b/libc/calls/utimensat.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -49,10 +50,10 @@ int utimensat(int dirfd, const char *path, const struct timespec ts[2], } if (ts) { STRACE("utimensat(%s, %#s, {{%,ld, %,ld}, {%,ld, %,ld}}, %#b) → %d% m", - __strace_dirfd(buf, dirfd), path, ts[0].tv_sec, ts[0].tv_nsec, + DescribeDirfd(buf, dirfd), path, ts[0].tv_sec, ts[0].tv_nsec, ts[1].tv_sec, ts[1].tv_nsec, flags, rc); } else { - STRACE("utimensat(%s, %#s, 0, %#b) → %d% m", __strace_dirfd(buf, dirfd), + STRACE("utimensat(%s, %#s, 0, %#b) → %d% m", DescribeDirfd(buf, dirfd), path, flags, rc); } return rc; diff --git a/libc/calls/wincrash_init.S b/libc/calls/wincrash_init.S index 68274ae48..33e0888fb 100644 --- a/libc/calls/wincrash_init.S +++ b/libc/calls/wincrash_init.S @@ -16,11 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" #include "libc/macros.internal.h" .init.start 300,_init_wincrash +#if !IsTiny() mov __wincrashearly(%rip),%rcx ntcall __imp_RemoveVectoredExceptionHandler +#endif pushpop 1,%rcx ezlea __wincrash_nt,dx ntcall __imp_AddVectoredExceptionHandler diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 08bc6df0c..0f574b732 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -22,6 +22,7 @@ #include "libc/calls/strace.internal.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" @@ -74,13 +75,9 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { #if defined(SYSDEBUG) && _DATATRACE if (__strace > 0) { - if (rc == -1 && errno == EFAULT) { - STRACE("writev(%d, %p, %d) → %'zd% m", fd, iov, iovlen, rc); - } else { - kprintf(STRACE_PROLOGUE "writev(%d, ", fd); - __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d) → %'ld% m\n", iovlen, rc); - } + kprintf(STRACE_PROLOGUE "writev(%d, ", fd); + DescribeIov(iov, iovlen, rc != -1 ? rc : 0); + kprintf(", %d) → %'ld% m\n", iovlen, rc); } #endif diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 582a59dee..c6145da37 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -1282,7 +1282,10 @@ void __asan_map_shadow(uintptr_t p, size_t n) { int prot, flag; struct DirectMap sm; struct MemoryIntervals *m; - assert(!OverlapsShadowSpace((void *)p, n)); + if (OverlapsShadowSpace((void *)p, n)) { + kprintf("error: %p size %'zu overlaps shadow space\n", p, n); + _Exit(1); + } m = weaken(_mmi); a = (0x7fff8000 + (p >> 3)) >> 16; b = (0x7fff8000 + (p >> 3) + (n >> 3) + 0xffff) >> 16; diff --git a/libc/intrin/assertfail.c b/libc/intrin/assertfail.greg.c similarity index 98% rename from libc/intrin/assertfail.c rename to libc/intrin/assertfail.greg.c index 1be6fe260..0579ff244 100644 --- a/libc/intrin/assertfail.c +++ b/libc/intrin/assertfail.greg.c @@ -46,6 +46,7 @@ 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/calls/strace_dirfd.greg.c b/libc/intrin/describedirfd.greg.c similarity index 94% rename from libc/calls/strace_dirfd.greg.c rename to libc/intrin/describedirfd.greg.c index 7b60493a5..86a72cbcd 100644 --- a/libc/calls/strace_dirfd.greg.c +++ b/libc/intrin/describedirfd.greg.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/sysv/consts/at.h" -privileged const char *__strace_dirfd(char buf[12], int dirfd) { +const char *DescribeDirfd(char buf[hasatleast 12], int dirfd) { if (dirfd == AT_FDCWD) return "AT_FDCWD"; FormatInt32(buf, dirfd); return buf; diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 521319d8a..2c822ac05 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -1,5 +1,11 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBEFLAGS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_INTRIN_DESCRIBEFLAGS_INTERNAL_H_ +#include "libc/calls/struct/iovec.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" #include "libc/nt/struct/securityattributes.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -15,8 +21,15 @@ const char *DescribeFlags(char *, size_t, struct DescribeFlags *, size_t, const char *DescribeMapFlags(int); const char *DescribeProtFlags(int); const char *DescribeRemapFlags(int); +const char *DescribeRlimitName(int); const char *DescribeSeccompOperationFlags(int); 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 *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 *); const char *DescribeNtPageFlags(uint32_t); const char *DescribeNtStartFlags(uint32_t); @@ -35,6 +48,8 @@ const char *DescribeNtConsoleModeOutputFlags(uint32_t); const char *DescribeNtFileFlagsAndAttributes(uint32_t); const char *DescribeNtSecurityAttributes(struct NtSecurityAttributes *); +void DescribeIov(const struct iovec *, int, ssize_t); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_INTRIN_DESCRIBEFLAGS_INTERNAL_H_ */ diff --git a/libc/calls/describeframe.c b/libc/intrin/describeframe.c similarity index 100% rename from libc/calls/describeframe.c rename to libc/intrin/describeframe.c diff --git a/libc/calls/strace_iov.greg.c b/libc/intrin/describeiov.greg.c similarity index 87% rename from libc/calls/strace_iov.greg.c rename to libc/intrin/describeiov.greg.c index 7c37fe0f0..3f8e32828 100644 --- a/libc/calls/strace_iov.greg.c +++ b/libc/intrin/describeiov.greg.c @@ -16,12 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" -void __strace_iov(const struct iovec *iov, int iovlen, ssize_t rem) { +void DescribeIov(const struct iovec *iov, int iovlen, ssize_t rem) { int i; + if ((!IsAsan() && kisdangerous(iov)) || + (IsAsan() && !__asan_is_valid(iov, iovlen * sizeof(struct iovec)))) { + kprintf("%p", iov); + return; + } kprintf("{"); for (i = 0; rem && i < MIN(5, iovlen); ++i) { kprintf( diff --git a/libc/intrin/describemapflags.greg.c b/libc/intrin/describemapflags.greg.c index 8b361b72a..f30c85fe9 100644 --- a/libc/intrin/describemapflags.greg.c +++ b/libc/intrin/describemapflags.greg.c @@ -25,6 +25,7 @@ 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_SHARED, "SHARED"}, // @@ -37,7 +38,6 @@ const char *DescribeMapFlags(int x) { {MAP_NORESERVE, "NORESERVE"}, // {MAP_NONBLOCK, "NONBLOCK"}, // {MAP_POPULATE, "POPULATE"}, // - {MAP_STACK, "STACK"}, // order matters }; return DescribeFlags(mapflags, sizeof(mapflags), kMapFlags, ARRAYLEN(kMapFlags), "MAP_", x); diff --git a/libc/calls/describemapping.c b/libc/intrin/describemapping.greg.c similarity index 96% rename from libc/calls/describemapping.c rename to libc/intrin/describemapping.greg.c index 9d2aceaa2..cbce1ac6d 100644 --- a/libc/calls/describemapping.c +++ b/libc/intrin/describemapping.greg.c @@ -28,6 +28,8 @@ static noasan char DescribeMapType(int flags) { return 'p'; case MAP_SHARED: return 's'; + case MAP_STACK: + return 'S'; default: return '?'; } @@ -46,7 +48,7 @@ noasan char *DescribeMapping(int prot, int flags, char p[hasatleast 8]) { DescribeProt(prot, p); p[3] = DescribeMapType(flags); p[4] = (flags & MAP_ANONYMOUS) ? 'a' : '-'; - p[5] = (flags & MAP_GROWSDOWN) ? 'S' : '-'; + p[5] = (flags & MAP_GROWSDOWN) ? 'G' : '-'; p[6] = (flags & MAP_FIXED) ? 'F' : '-'; p[7] = 0; return p; diff --git a/libc/calls/describeopenflags.greg.c b/libc/intrin/describeopenflags.greg.c similarity index 100% rename from libc/calls/describeopenflags.greg.c rename to libc/intrin/describeopenflags.greg.c diff --git a/libc/calls/strace_sigset.greg.c b/libc/intrin/describerlimit.greg.c similarity index 81% rename from libc/calls/strace_sigset.greg.c rename to libc/intrin/describerlimit.greg.c index f083d75c6..ecf233140 100644 --- a/libc/calls/strace_sigset.greg.c +++ b/libc/intrin/describerlimit.greg.c @@ -17,12 +17,19 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" -privileged const char *__strace_sigset(char buf[41], size_t bufsize, int rc, - const sigset_t *ss) { +const char *DescribeRlimit(char *buf, size_t bufsize, int rc, + const struct rlimit *rlim) { if (rc == -1) return "n/a"; - if (!ss) return "NULL"; - ksnprintf(buf, bufsize, "{%#lx, %#lx}", ss->__bits[0], ss->__bits[1]); + if (!rlim) return "NULL"; + if ((!IsAsan() && kisdangerous(rlim)) || + (IsAsan() && !__asan_is_valid(rlim, sizeof(*rlim)))) { + ksnprintf(buf, sizeof(buf), "%p", rlim); + } else { + ksnprintf(buf, bufsize, "{%'ld, %'ld}", rlim->rlim_cur, rlim->rlim_max); + } return buf; } diff --git a/libc/intrin/describerlimit_name.greg.c b/libc/intrin/describerlimit_name.greg.c new file mode 100644 index 000000000..35fc0bb21 --- /dev/null +++ b/libc/intrin/describerlimit_name.greg.c @@ -0,0 +1,47 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" +#include "libc/fmt/itoa.h" +#include "libc/sysv/consts/rlimit.h" + +const char *DescribeRlimitName(int resource) { + static char buf[12]; + if (resource == 127) return "n/a"; + if (resource == RLIMIT_AS) return "RLIMIT_AS"; + if (resource == RLIMIT_CPU) return "RLIMIT_CPU"; + if (resource == RLIMIT_FSIZE) return "RLIMIT_FSIZE"; + if (resource == RLIMIT_NPROC) return "RLIMIT_NPROC"; + if (resource == RLIMIT_NOFILE) return "RLIMIT_NOFILE"; + if (resource == RLIMIT_RSS) return "RLIMIT_RSS"; + if (resource == RLIMIT_DATA) return "RLIMIT_DATA"; + if (resource == RLIMIT_CORE) return "RLIMIT_CORE"; + if (resource == RLIMIT_STACK) return "RLIMIT_STACK"; + if (resource == RLIMIT_SIGPENDING) return "RLIMIT_SIGPENDING"; + if (resource == RLIMIT_MEMLOCK) return "RLIMIT_MEMLOCK"; + if (resource == RLIMIT_LOCKS) return "RLIMIT_LOCKS"; + if (resource == RLIMIT_MSGQUEUE) return "RLIMIT_MSGQUEUE"; + if (resource == RLIMIT_NICE) return "RLIMIT_NICE"; + if (resource == RLIMIT_RTPRIO) return "RLIMIT_RTPRIO"; + if (resource == RLIMIT_RTTIME) return "RLIMIT_RTTIME"; + if (resource == RLIMIT_SWAP) return "RLIMIT_SWAP"; + if (resource == RLIMIT_SBSIZE) return "RLIMIT_SBSIZE"; + if (resource == RLIMIT_NPTS) return "RLIMIT_NPTS"; + FormatInt32(buf, resource); + return buf; +} diff --git a/libc/intrin/describestat.greg.c b/libc/intrin/describestat.greg.c new file mode 100644 index 000000000..875ed656e --- /dev/null +++ b/libc/intrin/describestat.greg.c @@ -0,0 +1,85 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/kprintf.h" + +const char *DescribeStat(int rc, const struct stat *st) { + _Alignas(char) static char buf[300]; + int i, n; + + if (rc == -1) return "n/a"; + if (!st) return "NULL"; + if ((!IsAsan() && kisdangerous(st)) || + (IsAsan() && !__asan_is_valid(st, sizeof(*st)))) { + ksnprintf(buf, sizeof(buf), "%p", st); + return buf; + } + + i = 0; + n = sizeof(buf); + + i += ksnprintf(buf + i, n - i, "{.st_%s=%'ld", "size", st->st_size); + + if (st->st_blocks) { + i += + ksnprintf(buf + i, n - i, ", .st_blocks=%'lu/512", st->st_blocks * 512); + } + + if (st->st_mode) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%#o", "mode", st->st_mode); + } + + if (st->st_nlink != 1) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%'lu", "nlink", st->st_nlink); + } + + if (st->st_uid) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%lu", "uid", st->st_uid); + } + + if (st->st_gid) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%lu", "gid", st->st_gid); + } + + if (st->st_ino) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%lu", "ino", st->st_ino); + } + + if (st->st_gen) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%'lu", "gen", st->st_gen); + } + + if (st->st_flags) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%lx", "flags", st->st_flags); + } + + if (st->st_rdev) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%'lu", "rdev", st->st_rdev); + } + + if (st->st_blksize != PAGESIZE) { + i += ksnprintf(buf + i, n - i, ", .st_%s=%'lu", "blksize", st->st_blksize); + } + + buf[i++] = '}'; + + return buf; +} diff --git a/libc/calls/strace_timespec.greg.c b/libc/intrin/describetimespec.greg.c similarity index 81% rename from libc/calls/strace_timespec.greg.c rename to libc/intrin/describetimespec.greg.c index 2e7f89475..e9470461b 100644 --- a/libc/calls/strace_timespec.greg.c +++ b/libc/intrin/describetimespec.greg.c @@ -16,14 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" -#include "libc/calls/struct/timespec.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" -privileged const char *__strace_timespec(char buf[45], size_t bufsize, int rc, - const struct timespec *ts) { +const char *DescribeTimespec(char *buf, size_t bufsize, int rc, + const struct timespec *ts) { if (rc == -1) return "n/a"; if (!ts) return "NULL"; - ksnprintf(buf, bufsize, "{%ld, %ld}", ts->tv_sec, ts->tv_nsec); + if ((!IsAsan() && kisdangerous(ts)) || + (IsAsan() && !__asan_is_valid(ts, sizeof(*ts)))) { + ksnprintf(buf, bufsize, "%p", ts); + } else { + ksnprintf(buf, bufsize, "{%ld, %ld}", ts->tv_sec, ts->tv_nsec); + } return buf; } diff --git a/libc/fmt/formatint32.c b/libc/intrin/formatint32.c similarity index 100% rename from libc/fmt/formatint32.c rename to libc/intrin/formatint32.c diff --git a/libc/fmt/formatint64.c b/libc/intrin/formatint64.c similarity index 100% rename from libc/fmt/formatint64.c rename to libc/intrin/formatint64.c diff --git a/libc/intrin/getenv.c b/libc/intrin/getenv.greg.c similarity index 100% rename from libc/intrin/getenv.c rename to libc/intrin/getenv.greg.c diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 02b18b99b..50463291d 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -68,17 +68,22 @@ 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/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 \ @@ -88,6 +93,7 @@ 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 \ @@ -104,6 +110,10 @@ o/$(MODE)/libc/intrin/ntconsolemode.o: \ -ffreestanding \ $(NO_MAGIC) +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 += \ diff --git a/libc/calls/kopenflags.S b/libc/intrin/kopenflags.S similarity index 100% rename from libc/calls/kopenflags.S rename to libc/intrin/kopenflags.S diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 9595606ae..cc3681873 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -520,10 +520,13 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, type = 0; goto FormatString; } else { - if (p + 4 <= e) { + if (p + 7 <= e) { + *p++ = ' '; *p++ = 'e'; *p++ = 'r'; *p++ = 'r'; + *p++ = 'n'; + *p++ = 'o'; *p++ = '='; } type = 0; @@ -568,7 +571,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, if (!__replstderr || __nocolor) { break; } else { - s = "\r\033[K"; + s = "\r\e[K"; goto FormatString; } diff --git a/libc/bits/popcnt.c b/libc/intrin/popcnt.c similarity index 100% rename from libc/bits/popcnt.c rename to libc/intrin/popcnt.c diff --git a/libc/log/check.h b/libc/log/check.h index 15a3ecaf3..d59d08a69 100644 --- a/libc/log/check.h +++ b/libc/log/check.h @@ -24,19 +24,20 @@ COSMOPOLITAN_C_START_ #define DCHECK_NOTNULL(X, ...) \ __DCHK(ne, !=, NULL, "NULL", X, #X, "" __VA_ARGS__) -#define CHECK_ALIGNED(BYTES, VAR) \ - do { \ - if (((uintptr_t)VAR & ((BYTES)-1u))) { \ - __check_fail_aligned(BYTES, (uintptr_t)VAR); \ - unreachable; \ - } \ - VAR = (typeof(VAR))__builtin_assume_aligned(VAR, BYTES); \ +#define CHECK_ALIGNED(BYTES, VAR, ...) \ + do { \ + if (((uintptr_t)VAR & ((BYTES)-1u))) { \ + __check_fail_aligned(BYTES, (uintptr_t)VAR, __FILE__, __LINE__, \ + "" __VA_ARGS__); \ + unreachable; \ + } \ + VAR = (typeof(VAR))__builtin_assume_aligned(VAR, BYTES); \ } while (0) -#define DCHECK_ALIGNED(BYTES, VAR) \ +#define DCHECK_ALIGNED(BYTES, VAR, ...) \ do { \ if (((uintptr_t)VAR & ((BYTES)-1u))) { \ - __DCHK_ALIGNED(BYTES, (uintptr_t)VAR); \ + __DCHK_ALIGNED(BYTES, (uintptr_t)VAR, "" __VA_ARGS__); \ unreachable; \ } \ VAR = (typeof(VAR))__builtin_assume_aligned(VAR, BYTES); \ @@ -51,7 +52,8 @@ COSMOPOLITAN_C_START_ __check_fail(#SUFFIX, #OP, (uint64_t)Want, (WANTSTR), (uint64_t)Got, \ (GOTSTR), __FILE__, __LINE__, __VA_ARGS__); \ } else { \ - __check_fail_##SUFFIX((uint64_t)Want, (uint64_t)Got); \ + __check_fail_##SUFFIX((uint64_t)Want, (uint64_t)Got, __FILE__, \ + __LINE__, 0, __VA_ARGS__); \ } \ unreachable; \ } \ @@ -72,22 +74,30 @@ COSMOPOLITAN_C_START_ #endif /* NDEBUG */ #ifdef NDEBUG -#define __DCHK_ALIGNED(BYTES, VAR) +#define __DCHK_ALIGNED(BYTES, VAR, ...) #else -#define __DCHK_ALIGNED(BYTES, VAR) __check_fail_aligned(BYTES, VAR) +#define __DCHK_ALIGNED(BYTES, VAR, ...) \ + __check_fail_aligned(BYTES, VAR, __FILE__, __LINE__, __VA_ARGS__) #endif void __check_fail(const char *, const char *, uint64_t, const char *, uint64_t, const char *, const char *, int, const char *, ...) relegated wontreturn; -void __check_fail_eq(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_ne(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_le(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_lt(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_ge(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_gt(uint64_t, uint64_t) relegated wontreturn; -void __check_fail_aligned(unsigned, uint64_t) relegated wontreturn; +void __check_fail_eq(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_ne(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_le(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_lt(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_ge(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_gt(uint64_t, uint64_t, const char *, int, const char *, + const char *, ...) relegated wontreturn; +void __check_fail_aligned(unsigned, uint64_t, const char *, int, const char *, + ...) relegated wontreturn; #ifdef __VSCODE_INTELLISENSE__ #undef __CHK diff --git a/libc/log/checkaligned.c b/libc/log/checkaligned.c index 66fea2f58..f0bd5baaa 100644 --- a/libc/log/checkaligned.c +++ b/libc/log/checkaligned.c @@ -18,14 +18,16 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/stdio/stdio.h" -void __check_fail_aligned(unsigned bytes, uint64_t ptr) { +void __check_fail_aligned(unsigned bytes, uint64_t ptr, const char *file, + int line, const char *fmt, ...) { fflush(stderr); if (!IsTiny()) memsummary(fileno(stderr)); - (dprintf)(fileno(stderr), "%s%d%s%#p\n", "error: pointer not ", bytes, - "-byte aligned: ", ptr); + kprintf("%s:%d: error: pointer not %d-byte aligned: %p\n", file, line, bytes, + ptr); __die(); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index 410364304..4241a4680 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -16,12 +16,12 @@ │ 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/intrin/kprintf.h" #include "libc/log/internal.h" +#include "libc/log/log.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" /** * Handles failure of CHECK_xx() macros in -DNDEBUG mode. @@ -33,12 +33,22 @@ * * @see libc/log/thunks/__check_fail_ndebug.S */ -relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, - const char *opchar) { +relegated wontreturn void __check_fail_ndebug(uint64_t want, uint64_t got, + const char *file, int line, + const char *opchar, + const char *fmt, ...) { + va_list va; __restore_tty(); - (fprintf)(stderr, "\n%serror: %s: check failed: 0x%x %s 0x%x (%s)\n", - !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, - got, strerror(errno)); + kprintf("%rerror:%s:%d: check failed: %'ld %s %'ld% m", file, line, want, + opchar, got); + if (*fmt) { + kprintf(": "); + va_start(va, fmt); + kvprintf(fmt, va); + va_end(va); + } + kprintf("\n"); + if (weaken(__die)) weaken(__die)(); __restorewintty(); _Exit(68); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index b46452bd5..01733176b 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -201,7 +201,7 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, uname(&names); p = buf; errno = err; - kprintf("\n%serror%s: Uncaught %G (%s) on %s pid %d\n" + kprintf("\n%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n" " %s\n" " %m\n" " %s %s %s %s\n", @@ -210,8 +210,8 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + PAGESIZE)) ? "Stack Overflow" : GetSiCodeName(sig, si->si_code), - host, getpid(), program_invocation_name, names.sysname, names.version, - names.nodename, names.release); + host, getpid(), gettid(), program_invocation_name, names.sysname, + names.version, names.nodename, names.release); if (ctx) { kprintf("\n"); ShowFunctionCalls(ctx); diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 66edf6490..848c51c3b 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -19,6 +19,8 @@ #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -33,6 +35,10 @@ STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */ extern const unsigned char __oncrash_thunks[8][11]; +static void FreeSigaltstack(void *p) { + free(p); +} + /** * Installs crash signal handlers. * @@ -67,7 +73,7 @@ void ShowCrashReports(void) { ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; ss.ss_sp = malloc(SIGSTKSZ); - __cxa_atexit(free, ss.ss_sp, 0); + __cxa_atexit(FreeSigaltstack, ss.ss_sp, 0); sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; sigfillset(&sa.sa_mask); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { diff --git a/libc/log/thunks/__check_fail_eq.S b/libc/log/thunks/__check_fail_eq.S index a3acefd72..45a243b63 100644 --- a/libc/log/thunks/__check_fail_eq.S +++ b/libc/log/thunks/__check_fail_eq.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_EQ() in NDEBUG mode. __check_fail_eq: - loadstr "==",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_eq,globl + + .rodata.str1.1 +.Lop: .asciz "==" + .previous diff --git a/libc/log/thunks/__check_fail_ge.S b/libc/log/thunks/__check_fail_ge.S index 2c5daac54..3e758e309 100644 --- a/libc/log/thunks/__check_fail_ge.S +++ b/libc/log/thunks/__check_fail_ge.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_GE() in NDEBUG mode. __check_fail_ge: - loadstr ">=",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_ge,globl + + .rodata.str1.1 +.Lop: .asciz ">=" + .previous diff --git a/libc/log/thunks/__check_fail_gt.S b/libc/log/thunks/__check_fail_gt.S index 5b0e6ec9c..180744f67 100644 --- a/libc/log/thunks/__check_fail_gt.S +++ b/libc/log/thunks/__check_fail_gt.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_GT() in NDEBUG mode. __check_fail_gt: - loadstr ">",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_gt,globl + + .rodata.str1.1 +.Lop: .asciz ">" + .previous diff --git a/libc/log/thunks/__check_fail_le.S b/libc/log/thunks/__check_fail_le.S index ed9c7b754..61ade727d 100644 --- a/libc/log/thunks/__check_fail_le.S +++ b/libc/log/thunks/__check_fail_le.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_LE() in NDEBUG mode. __check_fail_le: - loadstr "<=",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_le,globl + + .rodata.str1.1 +.Lop: .asciz "<=" + .previous diff --git a/libc/log/thunks/__check_fail_lt.S b/libc/log/thunks/__check_fail_lt.S index 20d62aedb..3f7c826e7 100644 --- a/libc/log/thunks/__check_fail_lt.S +++ b/libc/log/thunks/__check_fail_lt.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_LT() in NDEBUG mode. __check_fail_lt: - loadstr "<",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_lt,globl + + .rodata.str1.1 +.Lop: .asciz "<" + .previous diff --git a/libc/log/thunks/__check_fail_ndebug.S b/libc/log/thunks/__check_fail_ndebug.S deleted file mode 100644 index 8ff4b7e23..000000000 --- a/libc/log/thunks/__check_fail_ndebug.S +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.internal.h" - -__check_fail_ndebug: - push %rbp - mov %rsp,%rbp - call ___check_fail_ndebug - pop %rbp - jmp __die # fewer elements in backtrace - .endfn __check_fail_ndebug,globl diff --git a/libc/log/thunks/__check_fail_ne.S b/libc/log/thunks/__check_fail_ne.S index aa9023b07..081bcb07d 100644 --- a/libc/log/thunks/__check_fail_ne.S +++ b/libc/log/thunks/__check_fail_ne.S @@ -21,6 +21,10 @@ // Code-size saving thunk for CHECK_NE() in NDEBUG mode. __check_fail_ne: - loadstr "!=",dx + lea .Lop(%rip),%r8 jmp __check_fail_ndebug .endfn __check_fail_ne,globl + + .rodata.str1.1 +.Lop: .asciz "!=" + .previous diff --git a/libc/nexgen32e/nt2sysv.S b/libc/nexgen32e/nt2sysv.S index 472e5f568..7f804f1a5 100644 --- a/libc/nexgen32e/nt2sysv.S +++ b/libc/nexgen32e/nt2sysv.S @@ -38,7 +38,6 @@ __nt2sysv: push %rdi push %rsi pushf - ezlea _base,bx lea -0x80(%rbp),%rdi call _savexmm mov %rcx,%rdi diff --git a/libc/runtime/cosmo.S b/libc/runtime/cosmo.S index df505099e..478e72a72 100644 --- a/libc/runtime/cosmo.S +++ b/libc/runtime/cosmo.S @@ -16,12 +16,12 @@ │ 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" #include "libc/notice.inc" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/map.h" #include "libc/calls/strace.internal.h" +#include "libc/sysv/consts/map.h" #include "libc/dce.h" .text.startup @@ -76,6 +76,44 @@ cosmo: push %rbp ret .endfn cosmo,weak +#if !IsTiny() + .init.start 304,_init_stack + testb IsWindows() + jnz 9f + push %rdi + push %rsi +// allocate stack + movabs $ape_stack_vaddr,%rdi + mov $ape_stack_memsz,%esi + mov $ape_stack_prot,%edx + mov $MAP_STACK,%ecx + or MAP_ANONYMOUS,%ecx + or $-1,%r8 + xor %r9d,%r9d + push %rsi + call mmap + pop %r8 + pop %rsi + pop %rdi + cmp $-1,%rax + je 9f +// switch stacks + leave + pop %rcx + lea (%rax,%r8),%rsp + sub $ape_stack_align,%rsp # openbsd:stackbound + mov %rbp,(%rsp) + push %rcx + push %rbp + mov %rsp,%rbp +9: nop + .init.end 304,_init_stack + .weak ape_stack_prot + .weak ape_stack_vaddr + .weak ape_stack_memsz + .weak ape_stack_align +#endif + #ifdef __PG__ .init.start 306,_init_ftrace push %rdi diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 4fd7fc4c5..217af382b 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -122,6 +122,15 @@ static textwindows void ViewOrDie(int64_t h, uint32_t access, size_t pos, } } +static textwindows int OnForkCrash(struct NtExceptionPointers *ep) { + kprintf("error: fork() child crashed!%n" + "\tExceptionCode = %#x%n" + "\tRip = %x%n", + ep->ExceptionRecord->ExceptionCode, + ep->ContextRecord ? ep->ContextRecord->Rip : -1); + ExitProcess(73); +} + textwindows void WinMainForked(void) { bool ok; jmp_buf jb; @@ -129,9 +138,9 @@ textwindows void WinMainForked(void) { char *addr, *shad; struct DirectMap dm; uint64_t size, upsize; - int64_t savetsc, savebir; struct MemoryInterval *maps; char16_t fvar[21 + 1 + 21 + 1]; + int64_t oncrash, savetsc, savebir; uint32_t i, varlen, oldprot, savepid; long mapcount, mapcapacity, specialz; extern uint64_t ts asm("kStartTsc"); @@ -142,6 +151,9 @@ textwindows void WinMainForked(void) { if (!varlen || varlen >= ARRAYLEN(fvar)) return; NTTRACE("WinMainForked()"); SetEnvironmentVariable(u"_FORK", NULL); +#ifdef SYSDEBUG + oncrash = AddVectoredExceptionHandler(1, NT2SYSV(OnForkCrash)); +#endif ParseInt(fvar, &reader); // read the cpu state from the parent process & plus @@ -167,7 +179,7 @@ textwindows void WinMainForked(void) { for (i = 0; i < mapcount; ++i) { addr = (char *)((uint64_t)maps[i].x << 16); size = maps[i].size; - if (maps[i].flags & MAP_PRIVATE) { + if ((maps[i].flags & MAP_TYPE) != MAP_SHARED) { upsize = ROUNDUP(size, FRAMESIZE); // we don't need to close the map handle because sys_mmap_nt // doesn't mark it inheritable across fork() for MAP_PRIVATE @@ -226,8 +238,13 @@ textwindows void WinMainForked(void) { } // restore the crash reporting stuff +#ifdef SYSDEBUG + RemoveVectoredExceptionHandler(oncrash); +#endif if (weaken(__wincrash_nt)) { - RemoveVectoredExceptionHandler(__wincrashearly); + if (!IsTiny()) { + RemoveVectoredExceptionHandler(__wincrashearly); + } AddVectoredExceptionHandler(1, (void *)weaken(__wincrash_nt)); } if (weaken(__onntconsoleevent_nt)) { @@ -290,7 +307,7 @@ textwindows int sys_fork_nt(void) { (_mmi.i * sizeof(_mmi.p[0])) >> 3); } for (i = 0; i < _mmi.i && ok; ++i) { - if (_mmi.p[i].flags & MAP_PRIVATE) { + if ((_mmi.p[i].flags & MAP_TYPE) != MAP_SHARED) { ok = WriteAll(writer, (void *)((uint64_t)_mmi.p[i].x << 16), _mmi.p[i].size); } diff --git a/libc/runtime/getcpucount.c b/libc/runtime/getcpucount.c index bde181330..7b870d806 100644 --- a/libc/runtime/getcpucount.c +++ b/libc/runtime/getcpucount.c @@ -36,9 +36,8 @@ static unsigned GetCpuCountLinux(void) { uint64_t s[16]; unsigned i, c, n; - if ((n = sched_getaffinity(0, sizeof(s), s)) > 0) { - assert(!(n & 7)); - for (n >>= 3, c = i = 0; i < n; ++i) { + if (!sched_getaffinity(0, sizeof(s), s)) { + for (c = i = 0; i < ARRAYLEN(s); ++i) { c += popcnt(s[i]); } return c; diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index c929999a8..e39f2f6fe 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -47,6 +47,12 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" +#define MAP_ANONYMOUS_linux 0x00000020 +#define MAP_ANONYMOUS_openbsd 0x00001000 +#define MAP_GROWSDOWN_linux 0x00000100 +#define MAP_STACK_freebsd 0x00000400 +#define MAP_STACK_openbsd 0x00004000 + #define IP(X) (intptr_t)(X) #define VIP(X) (void *)IP(X) #define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) @@ -122,20 +128,9 @@ noasan static size_t GetMemtrackSize(struct MemoryIntervals *mm) { return n; } -static noasan void *MapMemory(void *addr, size_t size, int prot, int flags, - int fd, int64_t off, int f, int x, int n) { - struct DirectMap dm; - dm = sys_mmap(addr, size, prot, f, fd, off); - if (UNLIKELY(dm.addr == MAP_FAILED)) { - if (IsWindows() && (flags & MAP_FIXED)) { - OnUnrecoverableMmapError( - "can't recover from MAP_FIXED errors on Windows"); - } - return MAP_FAILED; - } - if (UNLIKELY(dm.addr != addr)) { - OnUnrecoverableMmapError("KERNEL DIDN'T RESPECT MAP_FIXED"); - } +static noasan void *FinishMemory(void *addr, size_t size, int prot, int flags, + int fd, int64_t off, int f, int x, int n, + struct DirectMap dm) { if (!IsWindows() && (flags & MAP_FIXED)) { if (UntrackMemoryIntervals(addr, size)) { OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); @@ -154,6 +149,23 @@ static noasan void *MapMemory(void *addr, size_t size, int prot, int flags, return addr; } +static noasan void *MapMemory(void *addr, size_t size, int prot, int flags, + int fd, int64_t off, int f, int x, int n) { + struct DirectMap dm; + dm = sys_mmap(addr, size, prot, f, fd, off); + if (UNLIKELY(dm.addr == MAP_FAILED)) { + if (IsWindows() && (flags & MAP_FIXED)) { + OnUnrecoverableMmapError( + "can't recover from MAP_FIXED errors on Windows"); + } + return MAP_FAILED; + } + if (UNLIKELY(dm.addr != addr)) { + OnUnrecoverableMmapError("KERNEL DIDN'T RESPECT MAP_FIXED"); + } + return FinishMemory(addr, size, prot, flags, fd, off, f, x, n, dm); +} + /** * Maps memory from system, one frame at a time. * @@ -175,8 +187,8 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size, sz = size - m; dm = sys_mmap(addr + m, sz, prot, f, fd, oi); if (dm.addr == MAP_FAILED) return MAP_FAILED; - iscow = (flags & MAP_PRIVATE) && fd != -1; - readonlyfile = (flags & MAP_SHARED) && fd != -1 && + iscow = (flags & MAP_TYPE) != MAP_SHARED && fd != -1; + readonlyfile = (flags & MAP_TYPE) == MAP_SHARED && fd != -1 && (g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY; if (TrackMemoryInterval(&_mmi, x + (n - 1), x + (n - 1), dm.maphandle, prot, flags, readonlyfile, iscow, oi, sz) == -1) { @@ -208,6 +220,7 @@ 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; @@ -311,19 +324,68 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, return VIP(enomem()); } + needguard = false; p = (char *)ADDR(x); - if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ - dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); - if (dm.addr == MAP_FAILED) { - return MAP_FAILED; + if ((f & MAP_TYPE) == MAP_STACK) { + if (~f & MAP_ANONYMOUS) { + STRACE("MAP_STACK must be anonymous"); + return VIP(einval()); + } + f &= ~MAP_TYPE; + f |= MAP_PRIVATE; + if (IsOpenbsd()) { // openbsd:dubstack + // on openbsd this is less about scalability of threads, and more + // about defining the legal intervals for the RSP register. sadly + // openbsd doesn't let us create a new fixed stack mapping. but.. + // openbsd does allow us to overwrite existing fixed mappings, to + // authorize its usage as a stack. + if (sys_mmap(p, size, prot, f, fd, off).addr == MAP_FAILED) { + return MAP_FAILED; + } + f |= MAP_STACK_openbsd; + } else if (IsLinux()) { + // by default MAP_GROWSDOWN will auto-allocate 10mb of pages. it's + // supposed to stop growing if an adjacent allocation exists, to + // prevent your stacks from overlapping on each other. we're not + // able to easily assume a mapping beneath this one exists. even + // if we could, the linux kernel requires for muh security reasons + // that stacks be at least 1mb away from each other, so it's not + // possible to avoid this call if our goal is to have 60kb stacks + // with 4kb guards like a sane multithreaded production system. + // however this 1mb behavior oddly enough is smart enough to not + // apply if the mapping is a manually-created guard page. + if ((dm = sys_mmap(p + size - PAGESIZE, PAGESIZE, prot, + f | MAP_GROWSDOWN_linux, fd, off)) + .addr == MAP_FAILED) { + return MAP_FAILED; + } + sys_mmap(p, PAGESIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + dm.addr = p; + return FinishMemory(p, size, prot, flags, fd, off, f, x, n, dm); + } else { + if (IsFreebsd()) { + f |= MAP_STACK_freebsd; + } + needguard = true; } } if (!IsWindows()) { - return MapMemory(p, size, prot, flags, fd, off, f, x, n); + p = MapMemory(p, size, prot, flags, fd, off, f, x, n); } else { - return MapMemories(p, size, prot, flags, fd, off, f, x, n); + p = MapMemories(p, size, prot, flags, fd, off, f, x, n); } + + if (p != MAP_FAILED) { + if (needguard) { + if (IsWindows()) _spunlock(&_mmi.lock); + mprotect(p, PAGESIZE, PROT_NONE); + if (IsWindows()) _spinlock(&_mmi.lock); + } + } + + return p; } /** diff --git a/libc/runtime/mprotect.greg.c b/libc/runtime/mprotect.greg.c index 14b27b7db..b15e36c76 100644 --- a/libc/runtime/mprotect.greg.c +++ b/libc/runtime/mprotect.greg.c @@ -21,6 +21,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/prot.h" diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 21a40d890..74bc3e64d 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -268,7 +268,9 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, os = WINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */ ts = rdtsc(); __pid = GetCurrentProcessId(); +#if !IsTiny() __wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash); +#endif cmdline = GetCommandLine(); #ifdef SYSDEBUG /* sloppy flag-only check for early initialization */ diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index c688f808f..3c70b0e47 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -87,7 +87,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { kprintf(".control=%#.*hhs, ", msg->msg_controllen, msg->msg_control); if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); kprintf(".iov=", fd); - __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); + DescribeIov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); kprintf("}], %#x) → %'ld% m\n", flags, rc); } } diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index 3c8545229..9bd7a1ca7 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/iovec.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" @@ -45,6 +46,7 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { int64_t rc; char addr2[128]; struct msghdr msg2; + if (IsAsan() && !__asan_is_valid_msghdr(msg)) { rc = efault(); } else if (!IsWindows()) { @@ -77,22 +79,26 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { } else { rc = ebadf(); } + #if defined(SYSDEBUG) && _DATATRACE if (__strace > 0) { - if (!msg || (rc == -1 && errno == EFAULT)) { - DATATRACE("sendmsg(%d, %p, %#x) → %'ld% m", fd, msg, flags, rc); + kprintf(STRACE_PROLOGUE "sendmsg("); + if ((!IsAsan() && kisdangerous(msg)) || + (IsAsan() && !__asan_is_valid(msg, sizeof(*msg)))) { + kprintf("%p", msg); } else { - kprintf(STRACE_PROLOGUE "sendmsg(%d, {"); - if (msg->msg_namelen) - kprintf(".name=%#.*hhs, ", msg->msg_namelen, msg->msg_name); + kprintf("{"); + kprintf(".name=%#.*hhs, ", msg->msg_namelen, msg->msg_name); if (msg->msg_controllen) - kprintf(".control=%#.*hhs, ", msg->msg_controllen, msg->msg_control); + kprintf(", .control=%#.*hhs, ", msg->msg_controllen, msg->msg_control); if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); - kprintf(".iov=", fd); - __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); - kprintf("}, %#x) → %'ld% m\n", flags, rc); + kprintf(", .iov=", fd); + DescribeIov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); + kprintf("}"); } + kprintf(", %#x) → %'ld% m\n", flags, rc); } #endif + return rc; } diff --git a/libc/sysv/calls/sched_getaffinity.s b/libc/sysv/calls/sched_getaffinity.s deleted file mode 100644 index 09b2dc8a9..000000000 --- a/libc/sysv/calls/sched_getaffinity.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sched_getaffinity,0xfffffffffffff0cc,globl diff --git a/libc/sysv/calls/sys_sched_getaffinity.s b/libc/sysv/calls/sys_sched_getaffinity.s new file mode 100644 index 000000000..fd8de6319 --- /dev/null +++ b/libc/sysv/calls/sys_sched_getaffinity.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_sched_getaffinity,0xfffffffffffff0cc,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index b52068ef5..cc73c086b 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -221,20 +221,20 @@ syscon compat O_LARGEFILE 0 0 0 0 0 0 # syscon compat MAP_FILE 0 0 0 0 0 0 # consensus syscon mmap MAP_SHARED 1 1 1 1 1 1 # forced consensus & faked nt 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 0x0000010 0x0000010 0x0000010 0x0000010 0x0000010 0x0000010 # unix consensus; openbsd appears to forbid; faked nt -syscon mmap MAP_FIXED_NOREPLACE 0x8000000 0x8000000 0x8000000 0x8000000 0x8000000 0x8000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+ -syscon mmap MAP_ANONYMOUS 0x20 0x1000 0x0001000 0x1000 0x1000 0x20 # bsd consensus; faked nt -syscon mmap MAP_GROWSDOWN 0x0100 0 0x0000400 0x4000 0x4000 0x100000 # mandatory for OpenBSD stacks; MAP_STACK on Free/OpenBSD; MEM_TOP_DOWN on NT -syscon mmap MAP_CONCEAL 0 0 0x0020000 0x8000 0x8000 0 # omit from core dumps; MAP_NOCORE on FreeBSD -syscon mmap MAP_NORESERVE 0x4000 0x40 0 0 64 0 # Linux calls it "reserve"; NT calls it "commit"? which is default? -syscon mmap MAP_HUGETLB 0x040000 0 0 0 0 0x80000000 # kNtSecLargePages +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_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_NONBLOCK 0x00010000 0 0 0 0 0 +syscon mmap MAP_HUGETLB 0x00040000 0 0 0 0 0x80000000 # kNtSecLargePages syscon mmap MAP_HUGE_MASK 63 0 0 0 0 0 syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 0 -syscon mmap MAP_LOCKED 0x2000 0 0 0 0 0 -syscon mmap MAP_NONBLOCK 0x10000 0 0 0 0 0 -syscon mmap MAP_POPULATE 0x8000 0 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping -syscon mmap MAP_STACK 0x0100 0 0x0000400 0x4000 0x2000 0x100000 # use MAP_GROWSDOWN 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 diff --git a/libc/sysv/consts/MAP_ANONYMOUS.S b/libc/sysv/consts/MAP_ANONYMOUS.S index 9aa3781ea..b150f720f 100644 --- a/libc/sysv/consts/MAP_ANONYMOUS.S +++ b/libc/sysv/consts/MAP_ANONYMOUS.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_ANONYMOUS,0x20,0x1000,0x0001000,0x1000,0x1000,0x20 +.syscon mmap,MAP_ANONYMOUS,0x00000020,0x00001000,0x00001000,0x00001000,0x00001000,0x00000020 diff --git a/libc/sysv/consts/MAP_CONCEAL.S b/libc/sysv/consts/MAP_CONCEAL.S index 6f9de260c..a1a043dff 100644 --- a/libc/sysv/consts/MAP_CONCEAL.S +++ b/libc/sysv/consts/MAP_CONCEAL.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_CONCEAL,0,0,0x0020000,0x8000,0x8000,0 +.syscon mmap,MAP_CONCEAL,0,0,0x00020000,0x00008000,0x00008000,0 diff --git a/libc/sysv/consts/MAP_FIXED.S b/libc/sysv/consts/MAP_FIXED.S index e9dbc0e9e..7ad7d769e 100644 --- a/libc/sysv/consts/MAP_FIXED.S +++ b/libc/sysv/consts/MAP_FIXED.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_FIXED,0x0000010,0x0000010,0x0000010,0x0000010,0x0000010,0x0000010 +.syscon mmap,MAP_FIXED,0x00000010,0x00000010,0x00000010,0x00000010,0x00000010,0x00000010 diff --git a/libc/sysv/consts/MAP_FIXED_NOREPLACE.S b/libc/sysv/consts/MAP_FIXED_NOREPLACE.S index 2bcb31adf..90b4f3369 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,0x8000000,0x8000000,0x8000000,0x8000000,0x8000000,0x8000000 +.syscon mmap,MAP_FIXED_NOREPLACE,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000 diff --git a/libc/sysv/consts/MAP_GROWSDOWN.S b/libc/sysv/consts/MAP_GROWSDOWN.S index ec4438b6a..920b244c1 100644 --- a/libc/sysv/consts/MAP_GROWSDOWN.S +++ b/libc/sysv/consts/MAP_GROWSDOWN.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_GROWSDOWN,0x0100,0,0x0000400,0x4000,0x4000,0x100000 +.syscon mmap,MAP_GROWSDOWN,0x00000100,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_HUGETLB.S b/libc/sysv/consts/MAP_HUGETLB.S index 3d65c0afc..ba20aef3c 100644 --- a/libc/sysv/consts/MAP_HUGETLB.S +++ b/libc/sysv/consts/MAP_HUGETLB.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_HUGETLB,0x040000,0,0,0,0,0x80000000 +.syscon mmap,MAP_HUGETLB,0x00040000,0,0,0,0,0x80000000 diff --git a/libc/sysv/consts/MAP_LOCKED.S b/libc/sysv/consts/MAP_LOCKED.S index 1674c49d0..c16233884 100644 --- a/libc/sysv/consts/MAP_LOCKED.S +++ b/libc/sysv/consts/MAP_LOCKED.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_LOCKED,0x2000,0,0,0,0,0 +.syscon mmap,MAP_LOCKED,0x00002000,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_NONBLOCK.S b/libc/sysv/consts/MAP_NONBLOCK.S index 24604c007..5f13740b3 100644 --- a/libc/sysv/consts/MAP_NONBLOCK.S +++ b/libc/sysv/consts/MAP_NONBLOCK.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_NONBLOCK,0x10000,0,0,0,0,0 +.syscon mmap,MAP_NONBLOCK,0x00010000,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_NORESERVE.S b/libc/sysv/consts/MAP_NORESERVE.S index 2f0e391c1..a5ca8a6f1 100644 --- a/libc/sysv/consts/MAP_NORESERVE.S +++ b/libc/sysv/consts/MAP_NORESERVE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_NORESERVE,0x4000,0x40,0,0,64,0 +.syscon mmap,MAP_NORESERVE,0x00004000,0x00000040,0,0,0x00000040,0 diff --git a/libc/sysv/consts/MAP_POPULATE.S b/libc/sysv/consts/MAP_POPULATE.S index bc5e79c69..36d7843ec 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,0x8000,0,0,0,0,0 +.syscon mmap,MAP_POPULATE,0x00008000,0,0,0,0,0 diff --git a/libc/sysv/consts/MAP_STACK.S b/libc/sysv/consts/MAP_STACK.S index ed26f8ecf..35ef8bdf1 100644 --- a/libc/sysv/consts/MAP_STACK.S +++ b/libc/sysv/consts/MAP_STACK.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon mmap,MAP_STACK,0x0100,0,0x0000400,0x4000,0x2000,0x100000 +.syscon mmap,MAP_STACK,6,6,6,6,6,6 diff --git a/libc/sysv/consts/map.h b/libc/sysv/consts/map.h index 1378c4b2a..725ffac82 100644 --- a/libc/sysv/consts/map.h +++ b/libc/sysv/consts/map.h @@ -7,6 +7,7 @@ COSMOPOLITAN_C_START_ extern const long MAP_32BIT; extern const long MAP_ANON; extern const long MAP_ANONYMOUS; +extern const long MAP_CONCEAL; extern const long MAP_DENYWRITE; extern const long MAP_EXECUTABLE; extern const long MAP_FILE; @@ -21,7 +22,6 @@ extern const long MAP_NORESERVE; extern const long MAP_POPULATE; extern const long MAP_PRIVATE; extern const long MAP_SHARED; -extern const long MAP_CONCEAL; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ @@ -29,6 +29,7 @@ COSMOPOLITAN_C_END_ #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 @@ -36,6 +37,7 @@ COSMOPOLITAN_C_END_ #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) @@ -46,10 +48,8 @@ COSMOPOLITAN_C_END_ #define MAP_NONBLOCK SYMBOLIC(MAP_NONBLOCK) #define MAP_NORESERVE SYMBOLIC(MAP_NORESERVE) #define MAP_POPULATE SYMBOLIC(MAP_POPULATE) -#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL) #define MAP_ANON MAP_ANONYMOUS #define MAP_NOCORE MAP_CONCEAL -#define MAP_STACK MAP_GROWSDOWN #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MAP_H_ */ diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index bbd647557..8c4ff493d 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -234,7 +234,7 @@ scall lgetxattr 0x17bffffffffff0c0 globl scall llistxattr 0x17effffffffff0c3 globl scall lremovexattr 0x181ffffffffff0c6 globl scall sys_sched_setaffinity 0xfffffffffffff0cb globl hidden -scall sched_getaffinity 0xfffffffffffff0cc globl # returns bytes written on success. we polyfill bad posix designs like nice() returning 0, but we won't polyfill a bad unilateral redesign that's just glibc +scall sys_sched_getaffinity 0xfffffffffffff0cc globl hidden # returns bytes written on success. we polyfill bad posix designs like nice() returning 0, but we won't polyfill a bad unilateral redesign that's just glibc scall cpuset_getaffinity 0xffffff1e7fffffff globl scall cpuset_setaffinity 0xffffff1e8fffffff globl scall io_setup 0xfffffffffffff0ce globl diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index fd37a06f4..81147c104 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -20,9 +20,7 @@ #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/macros.h" -#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/nr.h" -#include "libc/sysv/consts/prot.h" /* ▄▄▄ ▄▄▄ ▀▓▓▒▄ @@ -355,107 +353,6 @@ _init_systemfive_pid: .endfn _init_systemfive_pid #endif #if SupportsSystemv() && !defined(TINY) - -// Create a stack with deterministic readable addresses. -// If ape_execve() already created us a stack that meets -// the requirements of STATIC_STACK_SIZE() then we skip. -_init_systemfive_stack: -#if SupportsWindows() || SupportsMetal() || SupportsOpenbsd() - testb $WINDOWS|METAL,__hostos(%rip) - jnz _init_systemfive_done -#endif - push %rdi - push %rsi - mov __NR_mmap,%eax - movabs $ape_stack_vaddr,%rdi - mov $ape_stack_memsz,%esi - mov $ape_stack_prot,%edx - mov $MAP_PRIVATE|MAP_FIXED,%r10d - or MAP_ANONYMOUS,%r10d - or $-1,%r8d - xor %r9d,%r9d - push %rdi # vaddr of stack - push %rsi # size of stack - push %r9 # openbsd:pad - push %r9 # openbsd:align -#if SupportsOpenbsd() - testb IsOpenbsd() - jz 0f - syscall # openbsd:dubstack - jc 1f - mov __NR_mmap,%eax -#endif -0: or MAP_GROWSDOWN,%r10d # openbsd:mapstack - clc - syscall - pop %r9 - pop %r9 - pop %r9 # size of stack - pop %r11 # vaddr of stack - jnc 2f -1: mov %eax,%edi - mov __NR_exit_group,%eax - syscall -2: test %rax,%rax - js 1b - -// prevent operating system from auto-mapping stack -// we guarantee stack overflows are always detected -// so long as you never use -DSTACK_FRAME_UNLIMITED -// TODO: Why does this fail sometimes with FreeBSD? - testb IsFreebsd() - jnz 9f - push %rax - push %rdx - push %r11 - mov __NR_mprotect,%eax - mov $PAGESIZE,%esi - xor %edx,%edx # PROT_NONE - syscall - pop %r11 - pop %rdx - pop %rax -9: - -// update the memory intervals -// m.i 0 4 -// m.n 8 4 -// m.p 16 8 -// m.p[0].x 24 4 -// m.p[0].y 28 4 -// m.p[0].h 32 8 -// m.p[0].prot 40 4 -// m.p[0].flags 44 4 -// m.p[0].offset 48 8 -// m.p[0].size 56 8 - .weak _mmi - ezlea _mmi,cx - test %rcx,%rcx - jz 3f - push %r9 # save the stack size - lea -1(%r11,%r9),%r9 # need incl. interval - shr $16,%r11 # for the stack range - shr $16,%r9 - movb $1,(%rcx) # _mmi.i - mov %r11d,24(%rcx) # _mmi.s[0].x - mov %r9d,28(%rcx) # _mmi.s[0].y - orq $-1,32(%rcx) # _mmi.s[0].h - mov %edx,40(%rcx) # _mmi.s[0].prot - mov %r10d,44(%rcx) # _mmi.s[0].flags - pop %r9 # restore stack size - mov %r9,56(%rcx) # _mmi.s[0].size -3: pop %rsi - pop %rdi - leave -// switch stacks - pop %rcx - lea (%rax,%r9),%rsp - sub $ape_stack_align,%rsp # openbsd:stackbound - mov %rbp,(%rsp) - push %rcx - push %rbp - mov %rsp,%rbp -// 𝑠𝑙𝑖𝑑𝑒 _init_systemfive_syscall: mov __NR_msyscall,%eax # syscall origin protect cmp $0xfff,%ax # openbsd is pretty cool @@ -556,8 +453,3 @@ syscon_windows:/* .asciz " %'u magnums loaded on %s\n" .previous #endif /* DEBUGSYS */ - - .weak ape_stack_prot - .weak ape_stack_vaddr - .weak ape_stack_memsz - .weak ape_stack_align diff --git a/libc/thread/clone.c b/libc/thread/clone.c index 616f63712..bdb666c32 100644 --- a/libc/thread/clone.c +++ b/libc/thread/clone.c @@ -47,7 +47,7 @@ STATIC_YOINK("gettid"); // for kprintf() #define __NR_thr_new 455 -#define __NR_sys___tfork 8 +#define __NR___tfork 8 #define __NR_clone_linux 56 #define __NR__lwp_create 309 #define __NR_getcontext_netbsd 307 @@ -67,6 +67,7 @@ static struct Cloner { } __cloner; static textwindows uint32_t WinThreadMain(void *notused) { + intptr_t rdi, rdx; int (*func)(void *); void *arg, *stack; struct WinThread *wt; @@ -82,16 +83,18 @@ static textwindows uint32_t WinThreadMain(void *notused) { wt->pid = tid; TlsSetValue(__winthread, wt); if (flags & CLONE_CHILD_SETTID) *ctid = tid; - asm volatile("mov\t%%rbp,%%rbx\n\t" + asm volatile("push\t%%rbp\n\t" "mov\t%%rsp,%%r15\n\t" "xor\t%%ebp,%%ebp\n\t" "xchg\t%%rax,%%rsp\n\t" "call\t*%2\n\t" "mov\t%%rbx,%%rbp\n\t" - "mov\t%%r15,%%rsp" - : "=a"(exitcode) - : "0"(stack), "d"(func), "D"(arg) - : "rbx", "r15", "memory"); + "mov\t%%r15,%%rsp\n\t" + "pop\t%%rbp" + : "=a"(exitcode), "=D"(rdi), "=d"(rdx) + : "0"(stack), "1"(arg), "2"(func) + : "rbx", "rcx", "rsi", "r8", "r9", "r10", "r11", "r15", + "memory"); if (flags & CLONE_CHILD_CLEARTID) *ctid = 0; __releasefd(tid); free(wt); @@ -113,8 +116,7 @@ static textwindows int CloneWindows(int (*func)(void *), void *stk, __cloner.ctid = ctid; __cloner.flags = flags; __cloner.stack = (char *)stk + stksz; - if (!(hand = CreateThread(&kNtIsInheritable, 0, NT2SYSV(WinThreadMain), 0, 0, - &wintid))) { + if (!(hand = CreateThread(0, 0, NT2SYSV(WinThreadMain), 0, 0, &wintid))) { _spunlock(&__cloner.lock); return -1; } @@ -132,6 +134,7 @@ static dontinline wontreturn void BsdThreadMain(void *unused) { void *arg; int (*func)(void *); int tid, flags, exitcode, *ctid; + asm("xor\t%ebp,%ebp"); tid = __cloner.tid; arg = __cloner.arg; func = __cloner.func; @@ -193,26 +196,24 @@ static privileged noasan int CloneOpenbsd(int (*func)(void *), char *stk, asm volatile("" ::: "memory"); params.tf_tid = (int *)&__cloner.tid; params.tf_tcb = flags & CLONE_SETTLS ? tls : 0; - params.tf_stack = stk + stksz; + // we need openbsd:stackbound because openbsd kernel enforces rsp must + // be on interval [stack, stack+size) thus the top address is an error + // furthermore this needs to be allocated using MAP_STACK OR GROWSDOWN + params.tf_stack = (void *)((intptr_t)((char *)stk + stksz - 1) & -16); asm volatile(CFLAG_ASM("syscall") : CFLAG_CONSTRAINT(failed), "=a"(ax) - : "1"(__NR_sys___tfork), "D"(¶ms), "S"(sizeof(params)) - : "rcx", "r11", "memory", "cc"); - if (!failed) { - if (!ax) { - // this is the child thread - // we probably can't access local variables anymore - asm volatile("" ::: "memory"); - BsdThreadMain(0); - unreachable; - } else { - if (flags & CLONE_PARENT_SETTID) *ptid = ax; - return ax; - } - } else { + : "1"(__NR___tfork), "D"(¶ms), "S"(sizeof(params)) + : "r11", "memory", "cc"); + if (failed) { errno = ax; return -1; } + if (ax) { + if (flags & CLONE_PARENT_SETTID) *ptid = ax; + return ax; + } + BsdThreadMain(0); + unreachable; } static privileged noasan int CloneNetbsd(int (*func)(void *), void *stk, @@ -231,8 +232,8 @@ static privileged noasan int CloneNetbsd(int (*func)(void *), void *stk, errno = ax; return -1; } - stack = (void *)(((long)((char *)stk + stksz) & -16) - 8 * 3); - *(long *)stack = (long)_Exit1; + stack = (intptr_t *)((intptr_t)((char *)stk + stksz) & -16); + *--stack = (intptr_t)_Exit1; ctx.uc_link = 0; ctx.uc_mcontext.rip = (intptr_t)func; ctx.uc_mcontext.rdi = (intptr_t)arg; @@ -265,14 +266,15 @@ static privileged int CloneLinux(int (*func)(void *), void *stk, size_t stksz, 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; - stk = (void *)(((long)((char *)stk + stksz) & -16) - 8); - *(long *)stk = (long)arg; + stack = (intptr_t *)((long)((char *)stk + stksz) & -16); + *--stack = (long)arg; // push 1 asm volatile("syscall" : "=a"(ax) - : "0"(__NR_clone_linux), "D"(flags), "S"(stk), "d"(ptid), + : "0"(__NR_clone_linux), "D"(flags), "S"(stack), "d"(ptid), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory"); if (ax > -4096u) { @@ -281,8 +283,8 @@ static privileged int CloneLinux(int (*func)(void *), void *stk, size_t stksz, } if (ax) return ax; asm volatile("xor\t%%ebp,%%ebp\n\t" - "pop\t%%rdi\n\t" - "call\t%0\n\t" + "pop\t%%rdi\n\t" // pop 1 + "call\t*%0\n\t" "xchg\t%%eax,%%edi\n\t" "jmp\t_Exit1" : /* no outputs */ @@ -302,6 +304,8 @@ static privileged int CloneLinux(int (*func)(void *), void *stk, size_t stksz, * @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 @@ -350,7 +354,8 @@ privileged int clone(int (*func)(void *), void *stk, size_t stksz, int flags, } // polyfill fork() and vfork() use case on platforms w/o clone - else if (flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) { + else if ((SupportsWindows() || SupportsBsd()) && + flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) { if (IsTiny()) { rc = einval(); } else if (!arg && !stksz) { @@ -358,7 +363,7 @@ privileged int clone(int (*func)(void *), void *stk, size_t stksz, int flags, } else { rc = einval(); } - } else if (flags == SIGCHLD) { + } else if ((SupportsWindows() || SupportsBsd()) && flags == SIGCHLD) { if (IsTiny()) { rc = eopnotsupp(); } else if (!arg && !stksz) { diff --git a/libc/thread/thread.mk b/libc/thread/thread.mk index ee0d6ddb1..04d2c7818 100644 --- a/libc/thread/thread.mk +++ b/libc/thread/thread.mk @@ -46,6 +46,7 @@ $(LIBC_THREAD_A).pkg: \ $(LIBC_THREAD_A_OBJS) \ $(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg) +# no red zone because asm("call") o/$(MODE)/libc/thread/clone.o: \ OVERRIDE_CFLAGS += \ -mno-red-zone diff --git a/libc/calls/strace_stat.greg.c b/test/libc/calls/sched_getaffinity_test.c similarity index 80% rename from libc/calls/strace_stat.greg.c rename to test/libc/calls/sched_getaffinity_test.c index 475e2c023..9edeb580b 100644 --- a/libc/calls/strace_stat.greg.c +++ b/test/libc/calls/sched_getaffinity_test.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,14 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" -#include "libc/intrin/kprintf.h" +#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/testlib/testlib.h" -privileged const char *__strace_stat(int rc, const struct stat *st) { - static char buf[256]; - if (rc == -1) return "n/a"; - if (!st) return "NULL"; - ksnprintf(buf, sizeof(buf), "{.st_size=%'ld, .st_mode=%#o, .st_ino=%'lu}", - st->st_size, st->st_mode, st->st_ino); - return buf; +void SetUp(void) { + if (!IsLinux()) { + exit(0); + } +} + +TEST(sched_getaffinity, test) { + uint64_t s[16]; + EXPECT_SYS(0, 0, sched_getaffinity(0, sizeof(s), &s)); } diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index 50672b6cc..fd91be5b1 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -28,6 +28,8 @@ #include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/clone.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" @@ -71,12 +73,10 @@ TEST(rand64, testLcg_doesntProduceIdenticalValues) { } TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { - char *stack; sigset_t ss, oldss; + void *stacks[THREADS]; int i, j, rc, ws, tid[THREADS]; if (IsXnu()) return; - if (IsNetbsd()) return; // still flaky :'( - if (IsOpenbsd()) return; // still flaky :'( struct sigaction oldsa; struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART}; EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa)); @@ -89,8 +89,9 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { } ready = false; for (i = 0; i < THREADS; ++i) { - stack = gc(malloc(GetStackSize())); - tid[i] = clone(Thrasher, stack, GetStackSize(), + stacks[i] = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + tid[i] = clone(Thrasher, stacks[i], FRAMESIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(intptr_t)i, 0, 0, 0, 0); ASSERT_NE(-1, tid[i]); @@ -108,4 +109,7 @@ 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], FRAMESIZE)); + } } diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 316517888..a777fdcc2 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -99,9 +99,8 @@ TEST(mmap, testMapFixed_destroysEverythingInItsPath) { TEST(mmap, customStackMemory_isAuthorized) { char *stack; uintptr_t w, r; - ASSERT_NE(MAP_FAILED, - (stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0))); + ASSERT_NE(MAP_FAILED, (stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_STACK, -1, 0))); asm("mov\t%%rsp,%0\n\t" "mov\t%2,%%rsp\n\t" "push\t%3\n\t" @@ -110,6 +109,7 @@ TEST(mmap, customStackMemory_isAuthorized) { : "=&r"(w), "=&r"(r) : "rm"(stack + STACKSIZE - 8), "i"(123)); ASSERT_EQ(123, r); + EXPECT_SYS(0, 0, munmap(stack, STACKSIZE)); } TEST(mmap, fileOffset) { diff --git a/test/libc/stdio/gz_test.c b/test/libc/stdio/gz_test.c new file mode 100644 index 000000000..395858021 --- /dev/null +++ b/test/libc/stdio/gz_test.c @@ -0,0 +1,45 @@ +/*-*- 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" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" +#include "third_party/zlib/zlib.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(gz, test) { + int fd; + gzFile f; + char buf[64] = {0}; + + ASSERT_NE(NULL, (f = gzopen("hello.txt.gz", "wb"))); + ASSERT_EQ(5, gzwrite(f, "hello", 5)); + ASSERT_EQ(Z_OK, gzclose(f)); + + ASSERT_NE(NULL, (f = gzopen("hello.txt.gz", "rb"))); + ASSERT_EQ(5, gzread(f, buf, sizeof(buf))); + ASSERT_STREQ("hello", buf); + ASSERT_EQ(Z_OK, gzclose(f)); + + ASSERT_NE(NULL, (fd = open("hello.txt.gz", O_RDONLY))); + ASSERT_EQ(25, read(fd, buf, sizeof(buf))); + EXPECT_BINEQ(u"▼ï◘      ♥╦H═╔╔• åª►6♣   ", buf); + ASSERT_SYS(0, 0, close(fd)); +} diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index c35d37464..468f42497 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -41,7 +41,8 @@ TEST_LIBC_STDIO_DIRECTDEPS = \ LIBC_UNICODE \ LIBC_X \ LIBC_ZIPOS \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ + THIRD_PARTY_ZLIB_GZ TEST_LIBC_STDIO_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_STDIO_DIRECTDEPS),$($(x)))) diff --git a/test/libc/thread/clone_test.c b/test/libc/thread/clone_test.c index e1d599b00..0566f8097 100644 --- a/test/libc/thread/clone_test.c +++ b/test/libc/thread/clone_test.c @@ -20,6 +20,8 @@ #include "libc/dce.h" #include "libc/intrin/spinlock.h" #include "libc/sysv/consts/clone.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" int x, thechilde; @@ -41,17 +43,18 @@ int thread(void *arg) { TEST(clone, test) { if (IsXnu()) return; - if (IsOpenbsd()) return; // still flaky :'( int me, tid; char *stack; me = gettid(); _spinlock(&lock); - stack = _gc(valloc(STACKSIZE)); - ASSERT_NE(-1, (tid = clone(thread, stack, STACKSIZE, + stack = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + ASSERT_NE(-1, (tid = clone(thread, stack, FRAMESIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)23, 0, 0, 0, 0))); _spinlock(&lock); ASSERT_EQ(42, x); ASSERT_NE(me, tid); ASSERT_EQ(tid, thechilde); + EXPECT_SYS(0, 0, munmap(stack, FRAMESIZE)); } diff --git a/test/tool/args/args_test.c b/test/tool/args/args_test.c new file mode 100644 index 000000000..71cb52b0f --- /dev/null +++ b/test/tool/args/args_test.c @@ -0,0 +1,101 @@ +/*-*- 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/testlib.h" +#include "tool/args/args.h" + +void FreeZipArgs(void); +int LoadZipArgsImpl(int *, char ***, char *); + +void TearDown(void) { + FreeZipArgs(); +} + +TEST(LoadZipArgs, testNoFile_noCliArgs_doesNothing) { + int argc = 1; + char *args[] = {"prog", 0}; + char **argv = &args[0]; + EXPECT_EQ(0, LoadZipArgsImpl(&argc, &argv, 0)); + ASSERT_EQ(1, argc); + EXPECT_STREQ("prog", argv[0]); + EXPECT_EQ(NULL, argv[1]); +} + +TEST(LoadZipArgs, testDefaultArgs_noCliArgs_replacesArgv) { + int argc = 1; + char *args[] = {"prog", 0}; + char **argv = &args[0]; + EXPECT_EQ(0, LoadZipArgsImpl(&argc, &argv, strdup("\ +-x\r\n\ +hello\r\n\ +-y\r\n\ +world\r\n\ +"))); + ASSERT_EQ(5, argc); + EXPECT_STREQ("prog", argv[0]); + EXPECT_STREQ("-x", argv[1]); + EXPECT_STREQ("hello", argv[2]); + EXPECT_STREQ("-y", argv[3]); + EXPECT_STREQ("world", argv[4]); + EXPECT_EQ(NULL, argv[5]); + EXPECT_EQ(5, __argc); + EXPECT_STREQ("prog", __argv[0]); + EXPECT_STREQ("-x", __argv[1]); + EXPECT_STREQ("hello", __argv[2]); + EXPECT_STREQ("-y", __argv[3]); + EXPECT_STREQ("world", __argv[4]); + EXPECT_EQ(NULL, __argv[5]); +} + +TEST(LoadZipArgs, testDefaultArgs_hasCliArgs_doesNothing) { + int argc = 2; + char *args[] = {"prog", "yo", 0}; + char **argv = &args[0]; + EXPECT_EQ(0, LoadZipArgsImpl(&argc, &argv, strdup("\ +-x\r\n\ +hello\r\n\ +-y\r\n\ +world\r\n\ +"))); + ASSERT_EQ(2, argc); + EXPECT_STREQ("prog", argv[0]); + EXPECT_STREQ("yo", argv[1]); + EXPECT_EQ(NULL, argv[2]); +} + +TEST(LoadZipArgs, testDots_hasCliArgs_mergesThem) { + int argc = 3; + char *args[] = {"prog", "yo", "dawg", 0}; + char **argv = &args[0]; + EXPECT_EQ(0, LoadZipArgsImpl(&argc, &argv, strdup("\ +-x\r\n\ +hell o\r\n\ +-y\r\n\ +world\r\n\ +...\r\n\ +"))); + ASSERT_EQ(7, argc); + EXPECT_STREQ("prog", argv[0]); + EXPECT_STREQ("-x", argv[1]); + EXPECT_STREQ("hell o", argv[2]); + EXPECT_STREQ("-y", argv[3]); + EXPECT_STREQ("world", argv[4]); + EXPECT_STREQ("yo", argv[5]); + EXPECT_STREQ("dawg", argv[6]); + EXPECT_EQ(NULL, argv[7]); +} diff --git a/test/tool/args/test.mk b/test/tool/args/test.mk new file mode 100644 index 000000000..ff9019a44 --- /dev/null +++ b/test/tool/args/test.mk @@ -0,0 +1,75 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TEST_TOOL_ARGS + +TEST_TOOL_ARGS = $(TOOL_ARGS_A_DEPS) $(TOOL_ARGS_A) +TEST_TOOL_ARGS_A = o/$(MODE)/test/tool/args/argstest.a +TEST_TOOL_ARGS_FILES := $(wildcard test/tool/args/*) +TEST_TOOL_ARGS_SRCS = $(filter %.c,$(TEST_TOOL_ARGS_FILES)) +TEST_TOOL_ARGS_SRCS_TEST = $(filter %_test.c,$(TEST_TOOL_ARGS_SRCS)) +TEST_TOOL_ARGS_HDRS = $(filter %.h,$(TEST_TOOL_ARGS_FILES)) +TEST_TOOL_ARGS_COMS = $(TEST_TOOL_ARGS_OBJS:%.o=%.com) + +TEST_TOOL_ARGS_OBJS = \ + $(TEST_TOOL_ARGS_SRCS:%.c=o/$(MODE)/%.o) + +TEST_TOOL_ARGS_COMS = \ + $(TEST_TOOL_ARGS_SRCS:%.c=o/$(MODE)/%.com) + +TEST_TOOL_ARGS_BINS = \ + $(TEST_TOOL_ARGS_COMS) \ + $(TEST_TOOL_ARGS_COMS:%=%.dbg) + +TEST_TOOL_ARGS_TESTS = \ + $(TEST_TOOL_ARGS_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + +TEST_TOOL_ARGS_CHECKS = \ + $(TEST_TOOL_ARGS_HDRS:%=o/$(MODE)/%.ok) \ + $(TEST_TOOL_ARGS_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + +TEST_TOOL_ARGS_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_LOG \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ + LIBC_TESTLIB \ + LIBC_UNICODE \ + LIBC_ZIPOS \ + LIBC_X \ + THIRD_PARTY_COMPILER_RT \ + TOOL_ARGS + +TEST_TOOL_ARGS_DEPS := \ + $(call uniq,$(foreach x,$(TEST_TOOL_ARGS_DIRECTDEPS),$($(x)))) + +$(TEST_TOOL_ARGS_A): \ + test/tool/args/ \ + $(TEST_TOOL_ARGS_A).pkg \ + $(TEST_TOOL_ARGS_OBJS) + +$(TEST_TOOL_ARGS_A).pkg: \ + $(TEST_TOOL_ARGS_OBJS) \ + $(foreach x,$(TEST_TOOL_ARGS_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/test/tool/args/%.com.dbg: \ + $(TEST_TOOL_ARGS_DEPS) \ + $(TEST_TOOL_ARGS_A) \ + o/$(MODE)/test/tool/args/%.o \ + $(TEST_TOOL_ARGS_A).pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE) + @$(APELINK) + +.PHONY: o/$(MODE)/test/tool/args +o/$(MODE)/test/tool/args: \ + $(TEST_TOOL_ARGS_BINS) \ + $(TEST_TOOL_ARGS_CHECKS) diff --git a/test/tool/test.mk b/test/tool/test.mk index 4b5f57a02..b776ecea4 100644 --- a/test/tool/test.mk +++ b/test/tool/test.mk @@ -3,6 +3,7 @@ .PHONY: o/$(MODE)/test/tool o/$(MODE)/test/tool: \ + o/$(MODE)/test/tool/args \ o/$(MODE)/test/tool/build \ o/$(MODE)/test/tool/plinko \ o/$(MODE)/test/tool/net \ diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 7125f0b85..4c0140ce2 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -140,7 +140,6 @@ #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -2498,7 +2497,7 @@ char *linenoiseWithHistory(const char *prompt, const char *prog) { fflush(stdout); if ((path = linenoiseGetHistoryPath(prog))) { if (linenoiseHistoryLoad(path) == -1) { - kprintf("%r%s: failed to load history: %m%n", path); + fprintf(stderr, "%s: failed to load history: %m\n", path); free(path); path = 0; } diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 3ff767466..73818ea0f 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -48,6 +48,7 @@ #include "third_party/lua/lrepl.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +#include "tool/args/args.h" // clang-format off asm(".ident\t\"\\n\\n\ @@ -388,8 +389,9 @@ static int pmain (lua_State *L) { int main (int argc, char **argv) { - int status, result; lua_State *L; + int status, result; + LoadZipArgs(&argc, &argv); if (IsModeDbg()) { ShowCrashReports(); } diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index e63b3fe92..e9915859f 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -38,7 +38,8 @@ THIRD_PARTY_LUA_DIRECTDEPS = \ LIBC_UNICODE \ NET_HTTP \ THIRD_PARTY_LINENOISE \ - THIRD_PARTY_GDTOA + THIRD_PARTY_GDTOA \ + TOOL_ARGS THIRD_PARTY_LUA_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_LUA_DIRECTDEPS),$($(x)))) diff --git a/third_party/make/job.c b/third_party/make/job.c index 96609776e..df958aa3e 100644 --- a/third_party/make/job.c +++ b/third_party/make/job.c @@ -1485,7 +1485,7 @@ load_too_high (void) } } - /* If we got here, something went wrong. Give up on this method. */ + /* If we 𝑔𝑜𝑡 𝑕𝑒𝑟𝑒, something went wrong. Give up on this method. */ if (r < 0) DB (DB_JOBS, ("Failed to read " LOADAVG ": %s\n", strerror (errno))); diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 846eb07c3..5ec41cbe7 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -462,7 +462,8 @@ THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \ TOOL_BUILD_LIB \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GETOPT \ - THIRD_PARTY_XED + THIRD_PARTY_XED \ + TOOL_ARGS THIRD_PARTY_PYTHON_STAGE1_A_DEPS = \ $(call uniq,$(foreach x,$(THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS),$($(x)))) @@ -1150,7 +1151,8 @@ THIRD_PARTY_PYTHON_STAGE2_A_DIRECTDEPS = \ THIRD_PARTY_PYTHON_STAGE1 \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_SQLITE3 \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ + TOOL_ARGS THIRD_PARTY_PYTHON_STAGE2_A_DEPS = \ $(call uniq,$(foreach x,$(THIRD_PARTY_PYTHON_STAGE2_A_DIRECTDEPS),$($(x)))) @@ -2102,7 +2104,8 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DIRECTDEPS = \ THIRD_PARTY_PYTHON_STAGE1 \ THIRD_PARTY_PYTHON_STAGE2 \ THIRD_PARTY_PYTHON_PYTEST \ - THIRD_PARTY_LINENOISE + THIRD_PARTY_LINENOISE \ + TOOL_ARGS THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DEPS = \ $(call uniq,$(foreach x,$(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_DIRECTDEPS),$($(x)))) @@ -4246,7 +4249,8 @@ THIRD_PARTY_PYTHON_PYTHON_DIRECTDEPS = \ THIRD_PARTY_LINENOISE \ THIRD_PARTY_PYTHON_STAGE1 \ THIRD_PARTY_PYTHON_STAGE2 \ - THIRD_PARTY_PYTHON_PYTEST + THIRD_PARTY_PYTHON_PYTEST \ + TOOL_ARGS o/$(MODE)/third_party/python/python.pkg: \ $(THIRD_PARTY_PYTHON_PYTHON_OBJS) \ diff --git a/third_party/python/repl.c b/third_party/python/repl.c index addf235ca..cbb5d7f76 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -41,6 +41,7 @@ #include "third_party/python/Include/pythonrun.h" #include "third_party/python/Include/unicodeobject.h" #include "third_party/python/Include/yoink.h" +#include "tool/args/args.h" /* clang-format off */ STATIC_STACK_SIZE(0x100000); @@ -348,5 +349,6 @@ RunPythonModule(int argc, char **argv) int main(int argc, char **argv) { + LoadZipArgs(&argc, &argv); return RunPythonModule(argc, argv); } diff --git a/third_party/quickjs/qjs.c b/third_party/quickjs/qjs.c index 595dae456..749e3b9eb 100644 --- a/third_party/quickjs/qjs.c +++ b/third_party/quickjs/qjs.c @@ -35,6 +35,7 @@ #include "third_party/gdtoa/gdtoa.h" #include "third_party/quickjs/cutils.h" #include "third_party/quickjs/quickjs-libc.h" +#include "tool/args/args.h" STATIC_STACK_SIZE(0x80000); @@ -329,6 +330,8 @@ int main(int argc, char **argv) #endif size_t stack_size = 0; + LoadZipArgs(&argc, &argv); + #if IsModeDbg() ShowCrashReports(); #endif diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 4749a6bc8..8088fecce 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -91,7 +91,8 @@ THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ - THIRD_PARTY_MUSL + THIRD_PARTY_MUSL \ + TOOL_ARGS THIRD_PARTY_QUICKJS_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_QUICKJS_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/sqlite3/shell.c b/third_party/sqlite3/shell.c index f3323e8b9..b49460ef6 100644 --- a/third_party/sqlite3/shell.c +++ b/third_party/sqlite3/shell.c @@ -92,6 +92,8 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.macros.h" +#include "tool/args/args.h" +#include "tool/args/args.h" #include "third_party/sqlite3/sqlite3.h" typedef sqlite3_int64 i64; @@ -20563,12 +20565,7 @@ static char *cmdline_option_value(int argc, char **argv, int i){ # endif #endif -#if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ -#else -int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ - char **argv; -#endif char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; @@ -20579,11 +20576,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ int nCmd = 0; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ -#if !SQLITE_SHELL_IS_UTF8 - char **argvToFree = 0; - int argcToFree = 0; -#endif + LoadZipArgs(&argc, &argv); setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ setvbuf(stdin, (char *)NULL, _IONBF, BUFSIZ); diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index 81b204bf0..90a1a02b7 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -62,7 +62,8 @@ THIRD_PARTY_SQLITE3_A_DIRECTDEPS = \ THIRD_PARTY_GDTOA \ THIRD_PARTY_LINENOISE \ THIRD_PARTY_MUSL \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ + TOOL_ARGS THIRD_PARTY_SQLITE3_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_SQLITE3_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/zlib/gz/gz.mk b/third_party/zlib/gz/gz.mk new file mode 100644 index 000000000..aa83ff83d --- /dev/null +++ b/third_party/zlib/gz/gz.mk @@ -0,0 +1,58 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += THIRD_PARTY_ZLIB_GZ + +THIRD_PARTY_ZLIB_GZ_ARTIFACTS += THIRD_PARTY_ZLIB_GZ_A +THIRD_PARTY_ZLIB_GZ = $(THIRD_PARTY_ZLIB_GZ_A_DEPS) $(THIRD_PARTY_ZLIB_GZ_A) +THIRD_PARTY_ZLIB_GZ_A = o/$(MODE)/third_party/zlib/gz/gz.a +THIRD_PARTY_ZLIB_GZ_A_FILES := $(wildcard third_party/zlib/gz/*) +THIRD_PARTY_ZLIB_GZ_A_HDRS = $(filter %.h,$(THIRD_PARTY_ZLIB_GZ_A_FILES)) +THIRD_PARTY_ZLIB_GZ_A_SRCS = $(filter %.c,$(THIRD_PARTY_ZLIB_GZ_A_FILES)) +THIRD_PARTY_ZLIB_GZ_A_OBJS = $(THIRD_PARTY_ZLIB_GZ_A_SRCS:%.c=o/$(MODE)/%.o) + +THIRD_PARTY_ZLIB_GZ_A_CHECKS = \ + $(THIRD_PARTY_ZLIB_GZ_A).pkg \ + $(THIRD_PARTY_ZLIB_GZ_A_HDRS:%=o/$(MODE)/%.ok) + +THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ + THIRD_PARTY_ZLIB + +THIRD_PARTY_ZLIB_GZ_A_DEPS := \ + $(call uniq,$(foreach x,$(THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS),$($(x)))) + +$(THIRD_PARTY_ZLIB_GZ_A): \ + third_party/zlib/gz/ \ + $(THIRD_PARTY_ZLIB_GZ_A).pkg \ + $(THIRD_PARTY_ZLIB_GZ_A_OBJS) + +$(THIRD_PARTY_ZLIB_GZ_A).pkg: \ + $(THIRD_PARTY_ZLIB_GZ_A_OBJS) \ + $(foreach x,$(THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS),$($(x)_A).pkg) + +$(THIRD_PARTY_ZLIB_GZ_OBJS): \ + OVERRIDE_CFLAGS += \ + -ffunction-sections \ + -fdata-sections + +THIRD_PARTY_ZLIB_GZ_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x))) +THIRD_PARTY_ZLIB_GZ_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_SRCS)) +THIRD_PARTY_ZLIB_GZ_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_HDRS)) +THIRD_PARTY_ZLIB_GZ_BINS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_BINS)) +THIRD_PARTY_ZLIB_GZ_CHECKS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_CHECKS)) +THIRD_PARTY_ZLIB_GZ_OBJS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_OBJS)) +$(THIRD_PARTY_ZLIB_GZ_OBJS): $(BUILD_FILES) third_party/zlib/gz/gz.mk + +.PHONY: o/$(MODE)/third_party/zlib/gz +o/$(MODE)/third_party/zlib/gz: \ + $(THIRD_PARTY_ZLIB_GZ_A) \ + $(THIRD_PARTY_ZLIB_GZ_CHECKS) diff --git a/third_party/zlib/gz/gzclose.c b/third_party/zlib/gz/gzclose.c new file mode 100644 index 000000000..951d14d5b --- /dev/null +++ b/third_party/zlib/gz/gzclose.c @@ -0,0 +1,27 @@ +#include "third_party/zlib/gz/gzguts.inc" +// clang-format off + +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +asm(".ident\t\"\\n\\n\ +zlib (zlib License)\\n\ +Copyright 1995-2017 Jean-loup Gailly and Mark Adler\""); +asm(".include \"libc/disclaimer.inc\""); + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int gzclose(file) + gzFile file; +{ + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +} diff --git a/third_party/zlib/gz/gzguts.inc b/third_party/zlib/gz/gzguts.inc new file mode 100644 index 000000000..740870758 --- /dev/null +++ b/third_party/zlib/gz/gzguts.inc @@ -0,0 +1,115 @@ +#include "libc/errno.h" +#include "libc/limits.h" +#include "libc/str/str.h" +#include "third_party/zlib/zlib.h" +// clang-format off + +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#define _POSIX_SOURCE + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +#define zstrerror() strerror(errno) + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + gzFile gzopen64(const char *, const char *); + int64_t gzseek64(gzFile, int64_t, int); + int64_t gztell64(gzFile); + int64_t gzoffset64(gzFile); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer (double-sized when writing) */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + int64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + int64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error (gz_statep, int, const char *); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror (DWORD error); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum int64_t + value -- needed when comparing unsigned to int64_t, which is signed + (possible int64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(int64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax (void); +# define GT_OFF(x) (sizeof(int) == sizeof(int64_t) && (x) > gz_intmax()) +#endif diff --git a/third_party/zlib/gz/gzlib.c b/third_party/zlib/gz/gzlib.c new file mode 100644 index 000000000..74f0123e5 --- /dev/null +++ b/third_party/zlib/gz/gzlib.c @@ -0,0 +1,510 @@ +#include "libc/calls/calls.h" +#include "libc/fmt/fmt.h" +#include "libc/limits.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "third_party/zlib/gz/gzguts.inc" +#include "third_party/zlib/zlib.h" +// clang-format off + +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +asm(".ident\t\"\\n\\n\ +zlib (zlib License)\\n\ +Copyright 1995-2017 Jean-loup Gailly and Mark Adler\""); +asm(".include \"libc/disclaimer.inc\""); + +/* Local functions */ +static void gz_reset(gz_statep); +static gzFile gz_open(const void *, int, const char *); + +/* Reset gzip file state */ +static void gz_reset(state) + gz_statep state; +{ + state->x.have = 0; /* no output data available */ + if (state->mode == GZ_READ) { /* for reading ... */ + state->eof = 0; /* not at end of file */ + state->past = 0; /* have not read past end yet */ + state->how = LOOK; /* look for gzip header */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->x.pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +static gzFile gz_open(path, fd, mode) + const void *path; + int fd; + const char *mode; +{ + gz_statep state; + size_t len; + int oflag; + int cloexec = 0; + int exclusive = 0; + + /* check input */ + if (path == NULL) + return NULL; + + /* allocate gzFile structure to return */ + state = (gz_statep)malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; + case 'e': + cloexec = 1; + break; + case 'x': + exclusive = 1; + break; + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + break; + case 'T': + state->direct = 1; + break; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + + /* save the path name for error messages */ + len = strlen((const char *)path); + state->path = (char *)malloc(len + 1); + if (state->path == NULL) { + free(state); + return NULL; + } + snprintf(state->path, len + 1, "%s", (const char *)path); + + /* compute the flags for open() */ + oflag = + (cloexec ? O_CLOEXEC : 0) | + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | + (exclusive ? O_EXCL : 0) | + (state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))); + + /* open the file with the appropriate flags (or just use fd) */ + state->fd = fd > -1 ? fd : ( + open((const char *)path, oflag, 0666)); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) { + lseek(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ + state->mode = GZ_WRITE; /* simplify later checks */ + } + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = lseek(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); +#else + sprintf(path, "<%s:%d>", "fd", fd); /* for debugging */ +#endif + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +int gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if ((size << 1) < size) + return -1; /* need to be able to double it */ + if (size < 2) + size = 2; /* need two bytes to check magic header */ + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* back up and start over */ + if (lseek(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +int64_t gzseek64(file, offset, whence) + gzFile file; + int64_t offset; + int whence; +{ + unsigned n; + int64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->x.pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->x.pos + offset >= 0) { + ret = lseek(state->fd, offset - state->x.have, SEEK_CUR); + if (ret == -1) + return -1; + state->x.have = 0; + state->eof = 0; + state->past = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->x.pos += offset; + return state->x.pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->x.pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->x.have) || (int64_t)state->x.have > offset ? + (unsigned)offset : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->x.pos + offset; +} + +/* -- see zlib.h -- */ +int64_t gzseek(file, offset, whence) + gzFile file; + int64_t offset; + int whence; +{ + int64_t ret; + + ret = gzseek64(file, (int64_t)offset, whence); + return ret == (int64_t)ret ? (int64_t)ret : -1; +} + +/* -- see zlib.h -- */ +int64_t gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->x.pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +int64_t gztell(file) + gzFile file; +{ + int64_t ret; + + ret = gztell64(file); + return ret == (int64_t)ret ? (int64_t)ret : -1; +} + +/* -- see zlib.h -- */ +int64_t gzoffset64(file) + gzFile file; +{ + int64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = lseek(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +int64_t gzoffset(file) + gzFile file; +{ + int64_t ret; + + ret = gzoffset64(file); + return ret == (int64_t)ret ? (int64_t)ret : -1; +} + +/* -- see zlib.h -- */ +int gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? state->past : 0; +} + +/* -- see zlib.h -- */ +const char * gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->err == Z_MEM_ERROR ? "out of memory" : + (state->msg == NULL ? "" : state->msg); +} + +/* -- see zlib.h -- */ +void gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) { + state->eof = 0; + state->past = 0; + } + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + if (err != Z_OK && err != Z_BUF_ERROR) + state->x.have = 0; + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, return literal string when requested */ + if (err == Z_MEM_ERROR) + return; + + /* construct error message with path */ + if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == + NULL) { + state->err = Z_MEM_ERROR; + return; + } + (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); +} diff --git a/third_party/zlib/gz/gzread.c b/third_party/zlib/gz/gzread.c new file mode 100644 index 000000000..73558600f --- /dev/null +++ b/third_party/zlib/gz/gzread.c @@ -0,0 +1,665 @@ +#include "libc/calls/calls.h" +#include "third_party/zlib/gz/gzguts.inc" +// clang-format off + +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +asm(".ident\t\"\\n\\n\ +zlib (zlib License)\\n\ +Copyright 1995-2017 Jean-loup Gailly and Mark Adler\""); +asm(".include \"libc/disclaimer.inc\""); + +/* Static functions */ +static int gz_load(gz_statep, unsigned char *, unsigned, unsigned *); +static int gz_avail(gz_statep); +static int gz_look(gz_statep); +static int gz_decomp(gz_statep); +static int gz_fetch(gz_statep); +static int gz_skip(gz_statep, int64_t); +static size_t gz_read(gz_statep, voidp, size_t); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +static int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + unsigned get, max = ((unsigned)-1 >> 2) + 1; + + *have = 0; + do { + get = len - *have; + if (get > max) + get = max; + ret = read(state->fd, buf + *have, get); + if (ret <= 0) + break; + *have += (unsigned)ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + If strm->avail_in != 0, then the current data is moved to the beginning of + the input buffer, and then the remainder of the buffer is loaded with the + available data from the input file. */ +static int gz_avail(state) + gz_statep state; +{ + unsigned got; + z_streamp strm = &(state->strm); + + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + if (state->eof == 0) { + if (strm->avail_in) { /* copy what's there to the start */ + unsigned char *p = state->in; + unsigned const char *q = strm->next_in; + unsigned n = strm->avail_in; + do { + *p++ = *q++; + } while (--n); + } + if (gz_load(state, state->in + strm->avail_in, + state->size - strm->avail_in, &got) == -1) + return -1; + strm->avail_in += got; + strm->next_in = state->in; + } + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression. If direct copying, then leftover input + data from the input buffer will be copied to the output buffer. In that + case, all further file reads will be directly to either the output buffer or + a user buffer. If decompressing, the inflate state will be initialized. + gz_look() will return 0 on success or -1 on failure. */ +static int gz_look(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = (unsigned char *)malloc(state->want); + state->out = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get at least the magic bytes in the input buffer */ + if (strm->avail_in < 2) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for gzip magic bytes -- if there, do gzip decoding (note: there is + a logical dilemma here when considering the case of a partially written + gzip file, to wit, if a single 31 byte is written, then we cannot tell + whether this is a single-byte file, or just a partially written gzip + file -- for here we assume that if a gzip file is being written, then + the header will be written in a single operation, so that reading a + single byte is sufficient indication that it is not a gzip file) */ + if (strm->avail_in > 1 && + strm->next_in[0] == 31 && strm->next_in[1] == 139) { + inflateReset(strm); + state->how = GZIP; + state->direct = 0; + return 0; + } + + /* no gzip header -- if we were decoding gzip before, then this is trailing + garbage. Ignore the trailing garbage and finish. */ + if (state->direct == 0) { + strm->avail_in = 0; + state->eof = 1; + state->x.have = 0; + return 0; + } + + /* doing raw i/o, copy any leftover input to output -- this assumes that + the output buffer is larger than the input buffer, which also assures + space for gzungetc() */ + state->x.next = state->out; + if (strm->avail_in) { + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + On return, state->x.have and state->x.next point to the just decompressed + data. If the gzip stream completes, state->how is reset to LOOK to look for + the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + on success, -1 on failure. */ +static int gz_decomp(state) + gz_statep state; +{ + int ret = Z_OK; + unsigned had; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_BUF_ERROR, "unexpected end of file"); + break; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output */ + state->x.have = had - strm->avail_out; + state->x.next = strm->next_out - state->x.have; + + /* if the gzip stream completed successfully, look for another */ + if (ret == Z_STREAM_END) + state->how = LOOK; + + /* good decompression */ + return 0; +} + +/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for to determine whether to copy or decompress. Returns -1 on error, + otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + end of the input file has been reached and all data has been processed. */ +static int gz_fetch(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + do { + switch(state->how) { + case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ + if (gz_look(state) == -1) + return -1; + if (state->how == LOOK) + return 0; + break; + case COPY: /* -> COPY */ + if (gz_load(state, state->out, state->size << 1, &(state->x.have)) + == -1) + return -1; + state->x.next = state->out; + return 0; + case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + } while (state->x.have == 0 && (!state->eof || strm->avail_in)); + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +static int gz_skip(state, len) + gz_statep state; + int64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->x.have) { + n = GT_OFF(state->x.have) || (int64_t)state->x.have > len ? + (unsigned)len : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + } + return 0; +} + +/* Read len bytes into buf from file, or less than len up to the end of the + input. Return the number of bytes read. If zero is returned, either the + end of file was reached, or there was an error. state->err must be + consulted in that case to determine which. */ +static size_t gz_read(state, buf, len) + gz_statep state; + voidp buf; + size_t len; +{ + size_t got; + unsigned n; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return 0; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* set n to the maximum amount of len that fits in an unsigned int */ + n = -1; + if (n > len) + n = len; + + /* first just try copying data from the output buffer */ + if (state->x.have) { + if (state->x.have < n) + n = state->x.have; + memcpy(buf, state->x.next, n); + state->x.next += n; + state->x.have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) { + state->past = 1; /* tried to read past end */ + break; + } + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || n < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return 0; + continue; /* no progress yet -- go back to copy above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + state->strm.avail_out = n; + state->strm.next_out = (unsigned char *)buf; + if (gz_decomp(state) == -1) + return 0; + n = state->x.have; + state->x.have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->x.pos += n; + } while (len); + + /* return number of bytes read into user buffer */ + return got; +} + +/* -- see zlib.h -- */ +int gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); + return -1; + } + + /* read len or fewer bytes to buf */ + len = gz_read(state, buf, len); + + /* check for an error */ + if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* return the number of bytes read (this is assured to fit in an int) */ + return (int)len; +} + +/* -- see zlib.h -- */ +size_t gzfread(buf, size, nitems, file) + voidp buf; + size_t size; + size_t nitems; + gzFile file; +{ + size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* read len or fewer bytes to buf, return the number of full items read */ + return len ? gz_read(state, buf, len) / size : 0; +} + +/* -- see zlib.h -- */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +#else +# undef gzgetc +# ifdef Z_CR_PREFIX_SET +# define gzgetc Cr_z_gzgetc +# endif +#endif + +int gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->x.have) { + state->x.have--; + state->x.pos++; + return *(state->x.next)++; + } + + /* nothing there -- try gz_read() */ + ret = gz_read(state, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +int gzgetc_(file) +gzFile file; +{ + return gzgetc(file); +} + +/* -- see zlib.h -- */ +int gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->x.have == 0) { + state->x.have = 1; + state->x.next = state->out + (state->size << 1) - 1; + state->x.next[0] = (unsigned char)c; + state->x.pos--; + state->past = 0; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->x.have == (state->size << 1)) { + gz_error(state, Z_DATA_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->x.next == state->out) { + unsigned char *src = state->out + state->x.have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->x.next = dest; + } + state->x.have++; + state->x.next--; + state->x.next[0] = (unsigned char)c; + state->x.pos--; + state->past = 0; + return c; +} + +/* -- see zlib.h -- */ +char * gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->x.have == 0 && gz_fetch(state) == -1) + return NULL; /* error */ + if (state->x.have == 0) { /* end of file */ + state->past = 1; /* read past end */ + break; /* return what we have */ + } + + /* look for end-of-line in current output buffer */ + n = state->x.have > left ? left : state->x.have; + eol = (unsigned char *)memchr(state->x.next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->x.next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->x.next, n); + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* return terminated string, or if nothing, end of file */ + if (buf == str) + return NULL; + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + + /* return 1 if transparent, 0 if processing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int gzclose_r(file) + gzFile file; +{ + int ret, err; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : err; +} diff --git a/third_party/zlib/gz/gzwrite.c b/third_party/zlib/gz/gzwrite.c new file mode 100644 index 000000000..f7c736704 --- /dev/null +++ b/third_party/zlib/gz/gzwrite.c @@ -0,0 +1,565 @@ +#include "libc/calls/calls.h" +#include "libc/fmt/fmt.h" +#include "third_party/zlib/gz/gzguts.inc" +// clang-format off + +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +asm(".ident\t\"\\n\\n\ +zlib (zlib License)\\n\ +Copyright 1995-2017 Jean-loup Gailly and Mark Adler\""); +asm(".include \"libc/disclaimer.inc\""); + +/* Static functions */ +static int gz_init(gz_statep); +static int gz_comp(gz_statep, int); +static int gz_zero(gz_statep, int64_t); +static size_t gz_write(gz_statep, voidpc, size_t); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on a memory allocation failure, or 0 on + success. */ +static int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input buffer (double size for gzprintf) */ + state->in = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = (unsigned char *)malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + strm->next_in = NULL; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file or if gz_init() + fails to allocate memory, otherwise 0. flush is assumed to be a valid + deflate() flush value. If flush is Z_FINISH, then the deflate() state is + reset to start a new gzip stream. If gz->direct is true, then simply write + to the output file without compressing, and ignore flush. */ +static int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, writ; + unsigned have, put, max = ((unsigned)-1 >> 2) + 1; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* write directly if requested */ + if (state->direct) { + while (strm->avail_in) { + put = strm->avail_in > max ? max : strm->avail_in; + writ = write(state->fd, strm->next_in, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in -= (unsigned)writ; + strm->next_in += writ; + } + return 0; + } + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + while (strm->next_out > state->x.next) { + put = strm->next_out - state->x.next > (int)max ? max : + (unsigned)(strm->next_out - state->x.next); + writ = write(state->fd, state->x.next, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + state->x.next += writ; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = state->out; + } + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on a write error or memory + allocation failure by gz_comp(), or 0 on success. */ +static int gz_zero(state, len) + gz_statep state; + int64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (int64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* Write len bytes from buf to file. Return the number of bytes written. If + the returned value is less than len, then there was an error. */ +static size_t gz_write(state, buf, len) + gz_statep state; + voidpc buf; + size_t len; +{ + size_t put = len; + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + unsigned have, copy; + + if (state->strm.avail_in == 0) + state->strm.next_in = state->in; + have = (unsigned)((state->strm.next_in + state->strm.avail_in) - + state->in); + copy = state->size - have; + if (copy > len) + copy = len; + memcpy(state->in + have, buf, copy); + state->strm.avail_in += copy; + state->x.pos += copy; + buf = (const char *)buf + copy; + len -= copy; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + state->strm.next_in = (const Bytef *)buf; + do { + unsigned n = (unsigned)-1; + if (n > len) + n = len; + state->strm.avail_in = n; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + len -= n; + } while (len); + } + + /* input was all buffered or compressed */ + return put; +} + +/* -- see zlib.h -- */ +int gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* write len bytes from buf (the return value will fit in an int) */ + return (int)gz_write(state, buf, len); +} + +/* -- see zlib.h -- */ +size_t gzfwrite(buf, size, nitems, file) + voidpc buf; + size_t size; + size_t nitems; + gzFile file; +{ + size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* write len bytes to buf, return the number of full items written */ + return len ? gz_write(state, buf, len) / size : 0; +} + +/* -- see zlib.h -- */ +int gzputc(file, c) + gzFile file; + int c; +{ + unsigned have; + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (have < state->size) { + state->in[have] = (unsigned char)c; + strm->avail_in++; + state->x.pos++; + return c & 0xff; + } + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = (unsigned char)c; + if (gz_write(state, buf, 1) != 1) + return -1; + return c & 0xff; +} + +/* -- see zlib.h -- */ +int gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* write string */ + len = strlen(str); + ret = gz_write(state, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +/* -- see zlib.h -- */ +int gzvprintf(gzFile file, const char *format, va_list va) +{ + int len; + unsigned left; + char *next; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return state->err; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); + next[state->size - 1] = 0; + len = (vsnprintf)(next, state->size, format, va); + + /* check that printf() results fit in buffer */ + if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) + return 0; + + /* update buffer and position, compress first half if past that */ + strm->avail_in += (unsigned)len; + state->x.pos += len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } + return len; +} + +int gzprintf(gzFile file, const char *format, ...) +{ + va_list va; + int ret; + + va_start(va, format); + ret = gzvprintf(file, format, va); + va_end(va); + return ret; +} + +/* -- see zlib.h -- */ +int gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* compress remaining data with requested flush */ + (void)gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return state->err; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int gzclose_w(file) + gzFile file; +{ + int ret = Z_OK; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + ret = state->err; + } + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1) + ret = state->err; + if (state->size) { + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + if (close(state->fd) == -1) + ret = Z_ERRNO; + free(state); + return ret; +} diff --git a/third_party/zlib/zlib.mk b/third_party/zlib/zlib.mk index b44e44f79..eebcda6c7 100644 --- a/third_party/zlib/zlib.mk +++ b/third_party/zlib/zlib.mk @@ -69,5 +69,6 @@ $(THIRD_PARTY_ZLIB_OBJS): $(BUILD_FILES) third_party/zlib/zlib.mk .PHONY: o/$(MODE)/third_party/zlib o/$(MODE)/third_party/zlib: \ + o/$(MODE)/third_party/zlib/gz \ $(THIRD_PARTY_ZLIB_A) \ $(THIRD_PARTY_ZLIB_CHECKS) diff --git a/tool/args/args.c b/tool/args/args.c new file mode 100644 index 000000000..204341f16 --- /dev/null +++ b/tool/args/args.c @@ -0,0 +1,114 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/x/x.h" +#include "tool/args/args.h" + +STATIC_YOINK("zip_uri_support"); + +static struct ZipArgs { + bool registered; + bool loaded; + int oldargc; + char *data; + char **args; + char **oldargv; +} g_zipargs; + +static void AddZipArg(int *argc, char ***argv, char *arg) { + *argv = xrealloc(*argv, (++(*argc) + 1) * sizeof(*(*argv))); + (*argv)[*argc - 1] = arg; + (*argv)[*argc - 0] = 0; +} + +void FreeZipArgs(void) { + if (g_zipargs.loaded) { + free(g_zipargs.data); + free(g_zipargs.args); + __argc = g_zipargs.oldargc; + __argv = g_zipargs.oldargv; + g_zipargs.loaded = false; + } +} + +int LoadZipArgsImpl(int *argc, char ***argv, char *data) { + int i, n, fd; + bool founddots; + char *arg, **args, *state, *start; + assert(!g_zipargs.loaded); + if (_chomp(data)) { + n = 0; + args = 0; + start = data; + founddots = false; + AddZipArg(&n, &args, (*argv)[0]); + while ((arg = strtok_r(start, "\r\n", &state))) { + if (!strcmp(arg, "...")) { + founddots = true; + for (i = 1; i < *argc; ++i) { + AddZipArg(&n, &args, (*argv)[i]); + } + } else { + AddZipArg(&n, &args, arg); + } + start = 0; + } + if (founddots || *argc <= 1) { + if (!g_zipargs.registered) { + atexit(FreeZipArgs); + g_zipargs.registered = 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; + __argv = args; + } else { + free(data); + free(args); + } + } + return 0; +} + +/** + * Replaces argument list with `/zip/.args` contents if it exists. + * + * Your `.args` file should have one argument per line. + * + * If the special argument `...` is *not* encountered, then the + * replacement will only happen if *no* CLI args are specified. + * + * 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 + */ +int LoadZipArgs(int *argc, char ***argv) { + return LoadZipArgsImpl(argc, argv, xslurp("/zip/.args", 0)); +} diff --git a/tool/args/args.h b/tool/args/args.h new file mode 100644 index 000000000..7b9a2cf3d --- /dev/null +++ b/tool/args/args.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_TOOL_ARGS_ARGS_H_ +#define COSMOPOLITAN_TOOL_ARGS_ARGS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int LoadZipArgs(int *, char ***); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_ARGS_ARGS_H_ */ diff --git a/tool/args/args.mk b/tool/args/args.mk new file mode 100644 index 000000000..0cd6dba4f --- /dev/null +++ b/tool/args/args.mk @@ -0,0 +1,47 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TOOL_ARGS + +TOOL_ARGS_ARTIFACTS += TOOL_ARGS_A +TOOL_ARGS = $(TOOL_ARGS_A_DEPS) $(TOOL_ARGS_A) +TOOL_ARGS_A = o/$(MODE)/tool/args/args.a +TOOL_ARGS_A_FILES := $(wildcard tool/args/*) +TOOL_ARGS_A_HDRS = $(filter %.h,$(TOOL_ARGS_A_FILES)) +TOOL_ARGS_A_SRCS = $(filter %.c,$(TOOL_ARGS_A_FILES)) +TOOL_ARGS_A_OBJS = $(TOOL_ARGS_A_SRCS:%.c=o/$(MODE)/%.o) + +TOOL_ARGS_A_CHECKS = \ + $(TOOL_ARGS_A_HDRS:%=o/$(MODE)/%.ok) \ + $(TOOL_ARGS_A).pkg + +TOOL_ARGS_A_DIRECTDEPS = \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_X \ + LIBC_ZIPOS + +TOOL_ARGS_A_DEPS := \ + $(call uniq,$(foreach x,$(TOOL_ARGS_A_DIRECTDEPS),$($(x)))) + +$(TOOL_ARGS_A): tool/args/ \ + $(TOOL_ARGS_A).pkg \ + $(TOOL_ARGS_A_OBJS) + +$(TOOL_ARGS_A).pkg: \ + $(TOOL_ARGS_A_OBJS) \ + $(foreach x,$(TOOL_ARGS_A_DIRECTDEPS),$($(x)_A).pkg) + +TOOL_ARGS_LIBS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x))) +TOOL_ARGS_SRCS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_SRCS)) +TOOL_ARGS_HDRS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_HDRS)) +TOOL_ARGS_BINS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_BINS)) +TOOL_ARGS_CHECKS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_CHECKS)) +TOOL_ARGS_OBJS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_OBJS)) +TOOL_ARGS_TESTS = $(foreach x,$(TOOL_ARGS_ARTIFACTS),$($(x)_TESTS)) + +.PHONY: o/$(MODE)/tool/args +o/$(MODE)/tool/args: $(TOOL_ARGS_CHECKS) diff --git a/tool/emacs/cosmo-asm-mode.el b/tool/emacs/cosmo-asm-mode.el index 3a9922746..0ff490313 100644 --- a/tool/emacs/cosmo-asm-mode.el +++ b/tool/emacs/cosmo-asm-mode.el @@ -353,10 +353,7 @@ (add-hook 'asm-mode-hook 'cosmo-asm-supplemental-hook) (setq asm-font-lock-keywords cosmo-asm-font-lock-keywords)) -;; Make -*-unix-assembly-*- mode line work correctly. -;; TODO(jart): Would be nice to use GitHub's name instead of changing asm-mode. -(defun unix-assembly-mode () - (interactive) - (asm-mode)) +;; Make -*-unix-assembly-*- mode line work correctly like GitHub. +(define-derived-mode unix-assembly-mode asm-mode "UNIX Assembly") (provide 'cosmo-asm-mode) diff --git a/tool/net/help.txt b/tool/net/help.txt index 5361f5201..76c083412 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -452,6 +452,41 @@ SPECIAL PATHS If you enable HTTPS client verification then redbean will check that HTTPS clients (a) have a certificate and (b) it was signed. + /.args + Specifies default command-line arguments. + + There's one argument per line. Trailing newline is ignored. If + the special argument `...` is *not* encountered, then the + replacement will only happen if *no* CLI args are specified. + If the special argument `...` *is* encountered, then it'll be + replaced with whatever CLI args were specified by the user. + + For example, you might want to use redbean.com in interpreter + mode, where your script file is inside the zip. Then, if your + redbean is run, what you want is to have the default behavior + be running your script. In that case, you might: + + $ cat <<'EOF' >.args + -i + /zip/hello.lua + EOF + + $ cat <<'EOF' >hello.lua + print("hello world") + EOF + + $ zip redbean.com .args hello.lua + $ ./redbean.com + hello world + + Please note that if you ran: + + $ ./redbean.com -vv + + Then the default mode of redbean will kick back in. To prevent + that from happening, simply add the magic arg `...` to the end + of your `.args` file. + ──────────────────────────────────────────────────────────────────────────────── HOOKS @@ -682,23 +717,26 @@ FUNCTIONS EscapeUser(str) → str Escapes URL username. See kescapeauthority.c. - Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) + Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}]) → status:int,{header:str=value:str,...},body:str Sends an HTTP/HTTPS request to the specified URL. If only the URL is provided, then a GET request is sent. If both URL and body parameters are specified, then a POST request is sent. If any other method needs to be specified (for example, PUT or DELETE), then passing a table as - the second value allows setting method and body values as well other - options: + the second value allows setting request method, body, and headers, as + well as some other options: - method (default = "GET"): sets the method to be used for the request. The specified method is converted to uppercase. - body (default = ""): sets the body value to be sent. + - headers: sets headers for the request using the key/value pairs + from this table. Only string keys are used and all the values are + converted to strings. - followredirect (default = true): forces temporary and permanent redirects to be followed. This behavior can be disabled by passing `false`. - maxredirects (default = 5): sets the number of allowed redirects to minimize looping due to misconfigured servers. When the number - is exceeded, the result of the last redirect is returned. + is exceeded, the last response is returned. When the redirect is being followed, the same method and body values are being sent in all cases except when 303 status is returned. In that case the method is set to GET and the body is removed before the @@ -1490,7 +1528,7 @@ UNIX MODULE `flags` should have one of: - - `O_RDONLY`: open for reading + - `O_RDONLY`: open for reading (default) - `O_WRONLY`: open for writing - `O_RDWR`: open for reading and writing @@ -2289,6 +2327,11 @@ UNIX MODULE - `SOCK_RDM` - `SOCK_SEQPACKET` + You may bitwise or any of the following into `type`: + + - `SOCK_CLOEXEC` + - `SOCK_NONBLOCK` + `protocol` defaults to `IPPROTO_TCP` and can be: - `IPPROTO_IP` @@ -2297,16 +2340,26 @@ UNIX MODULE - `IPPROTO_UDP` - `IPPROTO_RAW` - `SOCK_CLOEXEC` may be bitwise or'd into `type`. - unix.socketpair([family:int[, type:int[, protocol:int]]]) ├─→ fd1:int, fd2:int └─→ nil, unix.Errno - `SOCK_CLOEXEC` may be or'd into type - `family` defaults to `AF_INET` - `type` defaults to `SOCK_STREAM` - `protocol` defaults to `IPPROTO_TCP` + Creates bidirectional pipe. + + `family` defaults to `AF_UNIX`. + + `type` defaults to `SOCK_STREAM` and can be: + + - `SOCK_STREAM` + - `SOCK_DGRAM` + - `SOCK_SEQPACKET` + + You may bitwise or any of the following into `type`: + + - `SOCK_CLOEXEC` + - `SOCK_NONBLOCK` + + `protocol` defaults to `0`. unix.bind(fd:int[, ip:uint32, port:uint16]) ├─→ true diff --git a/tool/net/lunix.c b/tool/net/lunix.c index f5c69240a..5e615954e 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -436,11 +436,13 @@ static int LuaUnixChmod(lua_State *L) { static int LuaUnixReadlink(lua_State *L) { char *buf; ssize_t rc; - int olderr = errno; + const char *path; + int dirfd, olderr = errno; size_t got, bufsiz = 8192; + path = luaL_checkstring(L, 1); + dirfd = luaL_optinteger(L, 2, AT_FDCWD); if ((buf = LuaUnixAllocRaw(L, bufsiz))) { - if ((rc = readlinkat(luaL_optinteger(L, 2, AT_FDCWD), - luaL_checkstring(L, 1), buf, bufsiz)) != -1) { + if ((rc = readlinkat(dirfd, path, buf, bufsiz)) != -1) { got = rc; if (got < bufsiz) { lua_pushlstring(L, buf, got); @@ -459,9 +461,8 @@ static int LuaUnixReadlink(lua_State *L) { // ├─→ path:str // └─→ nil, unix.Errno static int LuaUnixGetcwd(lua_State *L) { - int olderr; char *path; - olderr = errno; + int olderr = errno; if ((path = getcwd(0, 0))) { lua_pushstring(L, path); free(path); @@ -849,7 +850,7 @@ static int LuaUnixFdatasync(lua_State *L) { return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1))); } -// unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) +// unix.open(path:str[, flags:int[, mode:int[, dirfd:int]]]) // ├─→ fd:int // └─→ nil, unix.Errno static int LuaUnixOpen(lua_State *L) { @@ -857,7 +858,7 @@ static int LuaUnixOpen(lua_State *L) { return SysretInteger( L, "open", olderr, openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), - luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); + luaL_optinteger(L, 2, O_RDONLY), luaL_optinteger(L, 3, 0))); } // unix.close(fd:int) @@ -944,7 +945,6 @@ static int LuaUnixWrite(lua_State *L) { fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); offset = luaL_optinteger(L, 3, -1); - size = MIN(size, 0x7ffff000); if (offset == -1) { rc = write(fd, data, size); } else { @@ -1147,9 +1147,9 @@ static int LuaUnixSocket(lua_State *L) { // └─→ nil, unix.Errno static int LuaUnixSocketpair(lua_State *L) { int sv[2], olderr = errno; - if (!socketpair(luaL_optinteger(L, 1, AF_INET), - luaL_optinteger(L, 2, SOCK_STREAM), - luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { + if (!socketpair(luaL_optinteger(L, 1, AF_UNIX), + luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, 0), + sv)) { lua_pushinteger(L, sv[0]); lua_pushinteger(L, sv[1]); return 2; @@ -1338,8 +1338,8 @@ static int LuaUnixAccept(lua_State *L) { static int LuaUnixPoll(lua_State *L) { size_t nfds; struct pollfd *fds, *fds2; - int i, fd, olderr, events, timeoutms; - olderr = errno; + int i, fd, events, timeoutms, olderr = errno; + timeoutms = luaL_optinteger(L, 2, -1); luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); for (fds = 0, nfds = 0; lua_next(L, 1);) { @@ -1358,7 +1358,6 @@ static int LuaUnixPoll(lua_State *L) { } lua_pop(L, 1); } - timeoutms = luaL_optinteger(L, 2, -1); olderr = errno; if ((events = poll(fds, nfds, timeoutms)) != -1) { lua_createtable(L, events, 0); @@ -1386,13 +1385,12 @@ static int LuaUnixRecvfrom(lua_State *L) { uint32_t addrsize; lua_Integer bufsiz; struct sockaddr_in sa; - int fd, flags, olderr; - olderr = errno; + int fd, flags, olderr = errno; addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); - flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); + flags = luaL_optinteger(L, 3, 0); if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); if (rc != -1) { @@ -1419,12 +1417,11 @@ static int LuaUnixRecv(lua_State *L) { size_t got; ssize_t rc; lua_Integer bufsiz; - int fd, flags, olderr, pushed; - olderr = errno; + int fd, flags, pushed, olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); - flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); + flags = luaL_optinteger(L, 3, 0); if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recv(fd, buf, bufsiz, flags); if (rc != -1) { @@ -1448,13 +1445,10 @@ static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; size_t sent, size; - lua_Integer bufsiz; - int fd, flags, olderr; - olderr = errno; + int fd, flags, olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); - size = MIN(size, 0x7ffff000); - flags = luaL_optinteger(L, 5, 0); + flags = luaL_optinteger(L, 3, 0); return SysretInteger(L, "send", olderr, send(fd, data, size, flags)); } @@ -1464,14 +1458,11 @@ static int LuaUnixSend(lua_State *L) { static int LuaUnixSendto(lua_State *L) { char *data; size_t sent, size; - lua_Integer bufsiz; - int fd, flags, olderr; struct sockaddr_in sa; - olderr = errno; - bzero(&sa, sizeof(sa)); + int fd, flags, olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); - size = MIN(size, 0x7ffff000); + bzero(&sa, sizeof(sa)); sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); sa.sin_port = htons(luaL_checkinteger(L, 4)); flags = luaL_optinteger(L, 5, 0); @@ -1493,12 +1484,10 @@ static int LuaUnixShutdown(lua_State *L) { // └─→ nil, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; - int i, n, how, olderr; - struct sigset *newmask, oldmask; - olderr = errno; - how = luaL_checkinteger(L, 1); - newmask = luaL_checkudata(L, 2, "unix.Sigset"); - if (!sigprocmask(how, newmask, &oldmask)) { + int olderr = errno; + struct sigset oldmask; + if (!sigprocmask(luaL_checkinteger(L, 1), + luaL_checkudata(L, 2, "unix.Sigset"), &oldmask)) { LuaPushSigset(L, oldmask); return 1; } else { @@ -1533,10 +1522,8 @@ static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) { // └─→ nil, unix.Errno static int LuaUnixSigaction(lua_State *L) { struct sigset *mask; - int i, n, sig, olderr; - struct sigaction sa, oldsa, *saptr; - saptr = &sa; - olderr = errno; + int i, n, sig, olderr = errno; + struct sigaction sa, oldsa, *saptr = &sa; sigemptyset(&sa.sa_mask); sig = luaL_checkinteger(L, 1); if (!(1 <= sig && sig <= NSIG)) { @@ -1611,13 +1598,7 @@ static int LuaUnixSigaction(lua_State *L) { // └─→ nil, unix.Errno static int LuaUnixSigsuspend(lua_State *L) { int olderr = errno; - struct sigset *set; - if (!lua_isnoneornil(L, 1)) { - set = luaL_checkudata(L, 1, "unix.Sigset"); - } else { - set = 0; - } - sigsuspend(set); + sigsuspend(!lua_isnoneornil(L, 1) ? luaL_checkudata(L, 1, "unix.Sigset") : 0); return SysretErrno(L, "sigsuspend", olderr); } @@ -1625,9 +1606,8 @@ static int LuaUnixSigsuspend(lua_State *L) { // ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int // └─→ nil, unix.Errno static int LuaUnixSetitimer(lua_State *L) { - int which, olderr; + int which, olderr = errno; struct itimerval it, oldit, *itptr; - olderr = errno; which = luaL_checkinteger(L, 1); if (!lua_isnoneornil(L, 2)) { itptr = ⁢ @@ -2266,7 +2246,7 @@ static int LuaUnixDirClose(lua_State *L) { int rc, olderr; dirp = GetUnixDirSelf(L); if (*dirp) { - olderr = 0; + olderr = errno; rc = closedir(*dirp); *dirp = 0; return SysretBool(L, "closedir", olderr, rc); @@ -2299,8 +2279,7 @@ static int LuaUnixDirRead(lua_State *L) { // ├─→ fd:int // └─→ nil, unix.Errno static int LuaUnixDirFd(lua_State *L) { - int fd, olderr; - olderr = errno; + int fd, olderr = errno; fd = dirfd(GetDirOrDie(L)); if (fd != -1) { lua_pushinteger(L, fd); @@ -2547,7 +2526,6 @@ int LuaUnix(lua_State *L) { // wait() options LuaSetIntField(L, "WNOHANG", WNOHANG); - LuaSetIntField(L, "WNOHANG", WNOHANG); // socket() family LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC); @@ -2561,6 +2539,7 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "SOCK_RDM", SOCK_RDM); LuaSetIntField(L, "SOCK_SEQPACKET", SOCK_SEQPACKET); LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC); + LuaSetIntField(L, "SOCK_NONBLOCK", SOCK_NONBLOCK); // socket() protocol LuaSetIntField(L, "IPPROTO_IP", IPPROTO_IP); diff --git a/tool/net/net.mk b/tool/net/net.mk index 3de3d5ec2..9b60d473c 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -66,6 +66,7 @@ TOOL_NET_DIRECTDEPS = \ THIRD_PARTY_REGEX \ THIRD_PARTY_SQLITE3 \ THIRD_PARTY_ZLIB \ + TOOL_ARGS \ TOOL_BUILD_LIB \ TOOL_DECODE_LIB diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 777b36463..ed7d895c7 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -146,6 +146,7 @@ #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" @@ -3685,7 +3686,7 @@ static int LuaFetch(lua_State *L) { bool usessl; uint32_t ip; struct Url url; - int t, ret, sock, methodidx; + int t, ret, sock, methodidx, hdridx; char *host, *port; struct TlsBio *bio; struct addrinfo *addr; @@ -3694,6 +3695,11 @@ static int LuaFetch(lua_State *L) { struct HttpUnchunker u; const char *urlarg, *request, *body, *method; char *conlenhdr = ""; + char *headers = 0; + char *hosthdr = 0; + char *agenthdr = brand; + char *key, *val, *hdr; + size_t keylen, vallen; size_t urlarglen, requestlen, paylen, bodylen; size_t g, i, n, hdrsize; int numredirects = 0, maxredirects = 5; @@ -3720,6 +3726,42 @@ static int LuaFetch(lua_State *L) { maxredirects = luaL_optinteger(L, -1, maxredirects); lua_getfield(L, 2, "numredirects"); numredirects = luaL_optinteger(L, -1, numredirects); + lua_getfield(L, 2, "headers"); + if (!lua_isnil(L, -1)) { + if (!lua_istable(L, -1)) + return luaL_argerror(L, 2, "invalid headers value; table expected"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { // skip any non-string keys + key = lua_tolstring(L, -2, &keylen); + if (!IsValidHttpToken(key, keylen)) + return luaL_argerror(L, 2, "invalid header name"); + + val = lua_tolstring(L, -1, &vallen); + if (!(hdr = gc(EncodeHttpHeaderValue(val, vallen, 0)))) + return luaL_argerror(L, 2, "invalid header value encoding"); + + // Content-Length and Connection will be overwritten; + // skip them to avoid duplicates; + // also allow unknown headers + if ((hdridx = GetHttpHeader(key, keylen)) == -1 || + hdridx != kHttpContentLength && hdridx != kHttpConnection) { + if (hdridx == kHttpUserAgent) { + agenthdr = hdr; + } else if (hdridx == kHttpHost) { + hosthdr = hdr; + } else { + appendd(&headers, key, keylen); + appendw(&headers, READ16LE(": ")); + appends(&headers, hdr); + appendw(&headers, READ16LE("\r\n")); + } + } + } + lua_pop(L, 1); // pop the value, keep the key for the next iteration + } + } lua_settop(L, 2); // drop all added elements to keep the stack balanced } else if (lua_isnoneornil(L, 2)) { body = ""; @@ -3765,15 +3807,18 @@ static int LuaFetch(lua_State *L) { port = usessl ? "443" : "80"; } } else { - ip = ntohl(servers.p[0].addr.sin_addr.s_addr); + ip = servers.n ? ntohl(servers.p[0].addr.sin_addr.s_addr) : INADDR_LOOPBACK; host = gc(xasprintf("%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16, ip >> 8, ip)); - port = gc(xasprintf("%d", ntohs(servers.p[0].addr.sin_port))); + port = + gc(xasprintf("%d", servers.n ? ntohs(servers.p[0].addr.sin_port) : 80)); } if (!IsAcceptableHost(host, -1)) { luaL_argerror(L, 1, "invalid host"); unreachable; } + if (!hosthdr) hosthdr = gc(xasprintf("%s:%s", host, port)); + url.fragment.p = 0, url.fragment.n = 0; url.scheme.p = 0, url.scheme.n = 0; url.user.p = 0, url.user.n = 0; @@ -3791,13 +3836,13 @@ static int LuaFetch(lua_State *L) { * Create HTTP message. */ request = gc(xasprintf("%s %s HTTP/1.1\r\n" - "Host: %s:%s\r\n" + "Host: %s\r\n" "Connection: close\r\n" "User-Agent: %s\r\n" - "%s" + "%s%s" "\r\n%s", - method, gc(EncodeUrl(&url, 0)), host, port, brand, - conlenhdr, body)); + method, gc(EncodeUrl(&url, 0)), hosthdr, agenthdr, + conlenhdr, headers ? headers : "", body)); requestlen = strlen(request); /* @@ -6740,14 +6785,19 @@ static uint32_t WindowsReplThread(void *arg) { return 0; } +static void InstallSignalHandler(int sig, void *handler) { + struct sigaction sa = {.sa_sigaction = handler}; + CHECK_NE(-1, sigaction(sig, &sa, 0)); +} + static void SigInit(void) { - xsigaction(SIGINT, OnInt, 0, 0, 0); - xsigaction(SIGHUP, OnHup, 0, 0, 0); - xsigaction(SIGTERM, OnTerm, 0, 0, 0); - xsigaction(SIGCHLD, OnChld, 0, 0, 0); - xsigaction(SIGUSR1, OnUsr1, 0, 0, 0); - xsigaction(SIGUSR2, OnUsr2, 0, 0, 0); - xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); + InstallSignalHandler(SIGINT, OnInt); + InstallSignalHandler(SIGHUP, OnHup); + InstallSignalHandler(SIGTERM, OnTerm); + InstallSignalHandler(SIGCHLD, OnChld); + InstallSignalHandler(SIGUSR1, OnUsr1); + InstallSignalHandler(SIGUSR2, OnUsr2); + InstallSignalHandler(SIGPIPE, SIG_IGN); /* TODO(jart): SIGXCPU and SIGXFSZ */ } @@ -6986,6 +7036,7 @@ void RedBean(int argc, char *argv[]) { } int main(int argc, char *argv[]) { + LoadZipArgs(&argc, &argv); if (!IsTiny()) { ShowCrashReports(); } diff --git a/tool/tool.mk b/tool/tool.mk index f41b34de6..1ddd5ff2c 100644 --- a/tool/tool.mk +++ b/tool/tool.mk @@ -3,6 +3,7 @@ .PHONY: o/$(MODE)/tool o/$(MODE)/tool: \ + o/$(MODE)/tool/args \ o/$(MODE)/tool/build \ o/$(MODE)/tool/decode \ o/$(MODE)/tool/hash \