diff --git a/Makefile b/Makefile index c89943786..f9aa33edb 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ # build/config.mk SHELL = /bin/sh -HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 xnu win7 win10 +HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 win7 win10 xnu SANITY := $(shell build/sanitycheck $$PPID) .SUFFIXES: @@ -107,7 +107,6 @@ include libc/fmt/fmt.mk #─┘ include libc/calls/calls.mk #─┐ include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME include libc/crt/crt.mk # │ You can issue system calls -include libc/thread/thread.mk # │ include libc/rand/rand.mk # │ include libc/unicode/unicode.mk # │ include third_party/dlmalloc/dlmalloc.mk #─┘ @@ -115,6 +114,7 @@ include libc/mem/mem.mk #─┐ include libc/zipos/zipos.mk # ├──DYNAMIC RUNTIME include third_party/gdtoa/gdtoa.mk # │ You can now use stdio include libc/time/time.mk # │ You can finally call malloc() +include libc/thread/thread.mk # │ include libc/alg/alg.mk # │ include libc/stdio/stdio.mk # │ include third_party/libcxx/libcxx.mk # │ @@ -177,6 +177,7 @@ include test/libc/intrin/test.mk include test/libc/mem/test.mk include test/libc/nexgen32e/test.mk include test/libc/runtime/test.mk +include test/libc/thread/test.mk include test/libc/sock/test.mk include test/libc/bits/test.mk include test/libc/str/test.mk diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 705f5c5f2..4d941f872 100755 Binary files a/build/bootstrap/compile.com and b/build/bootstrap/compile.com differ diff --git a/examples/crashreport2.c b/examples/crashreport2.c new file mode 100644 index 000000000..fab1424fd --- /dev/null +++ b/examples/crashreport2.c @@ -0,0 +1,52 @@ +#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/log/log.h" +#include "libc/stdio/stdio.h" + +/** + * @fileoverview CTRL+\ debugging example + * + * make -j8 -O o//examples/crashreport2.com + * o//examples/crashreport2.com + * + * Assuming you call ShowCrashReports() from main(), you can press + * `CTRL+\` at anny time to generate a `SIGQUIT` message that lets you + * debug wrongness and freezups. + * + * On supported platforms, this will cause GDB to automatically attach. + * The nice thing about this, is you can start stepping through your + * code at the precice instruction where the interrupt happened. See + * `libc/log/attachdebugger.c` to see how it works. + * + * If you wish to suppress the auto-GDB behavior, then: + * + * export GDB= + * + * Or alternatively: + * + * extern int __isworker; + * __isworker = true; + * + * Will cause your `SIGQUIT` handler to just print a crash report + * instead. This is useful for production software that might be running + * in a terminal environment like GNU Screen, but it's not desirable to + * have ten worker processes trying to attach GDB at once. + */ + +int main(int argc, char *argv[]) { + volatile int64_t x; + ShowCrashReports(); + printf("please press ctrl+\\ and see what happens...\n"); + sigsuspend(0); + printf("\n\n"); + printf("congratulations! your program is now resuming\n"); + return 0; +} diff --git a/examples/hangman.c b/examples/hangman.c index f3b508dd6..f580aa476 100644 --- a/examples/hangman.c +++ b/examples/hangman.c @@ -44,8 +44,8 @@ #include "libc/str/str.h" #include "libc/time/time.h" #include "third_party/zlib/zlib.h" +// clang-format off -/* clang-format off */ #define DICT "usr/share/dict/hangman" #define MAXERR 7 #define MINSCORE 0 diff --git a/examples/rlimit.c b/examples/rlimit.c new file mode 100644 index 000000000..c98aa4113 --- /dev/null +++ b/examples/rlimit.c @@ -0,0 +1,72 @@ +#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/strace.internal.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/color.internal.h" +#include "libc/macros.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/rlim.h" +#include "libc/sysv/consts/rlimit.h" + +/** + * @fileoverview tool for printing and changing system resource limits + * + * This is what you do if you want to not accidentally bomb your system + * with runaway code. If you haven't accidentally bombed your UNIX + * system before then you're not pushing it hard enough. + */ + +static void SetLimit(int resource, uint64_t soft, uint64_t hard) { + struct rlimit old; + struct rlimit lim = {soft, hard}; + if (resource == 127) return; + if (setrlimit(resource, &lim) == -1) { + if (!getrlimit(resource, &old)) { + lim.rlim_max = MIN(hard, old.rlim_max); + lim.rlim_cur = MIN(soft, lim.rlim_max); + if (!setrlimit(resource, &lim)) { + fprintf(stderr, "%snote: setrlimit(%s) downgraded to {%,ld, %,ld}\n", + __strace_rlimit_name(resource), lim.rlim_cur, lim.rlim_max); + return; + } + } + fprintf(stderr, "error: setrlimit(%s, %,ld, %,ld) failed %m%n", + __strace_rlimit_name(resource), soft, hard); + exit(1); + } +} + +int main(int argc, char *argv[]) { + int i, rc; + struct rlimit rlim; + + // // example of how you might change the limits + // SetLimit(RLIMIT_CPU, 3, 33); + // SetLimit(RLIMIT_NPROC, 4, 128); + // SetLimit(RLIMIT_NOFILE, 32, 128); + // SetLimit(RLIMIT_SIGPENDING, 16, 1024); + // SetLimit(RLIMIT_AS, 8 * 1024 * 1024, 1l * 1024 * 1024 * 1024); + // SetLimit(RLIMIT_RSS, 8 * 1024 * 1024, 1l * 1024 * 1024 * 1024); + // SetLimit(RLIMIT_DATA, 8 * 1024 * 1024, 1l * 1024 * 1024 * 1024); + // SetLimit(RLIMIT_FSIZE, 8 * 1000 * 1000, 1l * 1000 * 1000 * 1000); + + 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)); + } + + return 0; +} diff --git a/examples/ucontext-sigfpe-recovery.c b/examples/ucontext-sigfpe-recovery.c index 0c7c6491d..30965799f 100644 --- a/examples/ucontext-sigfpe-recovery.c +++ b/examples/ucontext-sigfpe-recovery.c @@ -16,12 +16,22 @@ #include "libc/sysv/consts/sig.h" #include "third_party/xed/x86.h" +/** + * @fileoverview How to change CPU state on signal delivery + * + * This program redefines division by zero so that it has a definition. + * The definition is the meaning of life, the universe, and everything. + * Normally crash signals like `SIGSEGV`, `SIGILL`, and `SIGFPE` aren't + * recoverable. This example shows how it actually can be done with Xed + * and this example should work on all supported platforms even Windows + */ + void handler(int sig, siginfo_t *si, ucontext_t *ctx) { struct XedDecodedInst xedd; xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); ctx->uc_mcontext.rip += xedd.length; - ctx->uc_mcontext.rax = 42; + ctx->uc_mcontext.rax = 42; // set the DIV result registers rdx:rax ctx->uc_mcontext.rdx = 0; } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 7eac94776..e50c44cdc 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -87,7 +87,6 @@ int chdir(const char *); int chmod(const char *, uint32_t); int chown(const char *, uint32_t, uint32_t); int chroot(const char *); -int clone(int (*)(void *), void *, int, void *, ...); int close(int); int closedir(DIR *); int creat(const char *, uint32_t); @@ -169,6 +168,7 @@ int sched_yield(void); int setegid(uint32_t); int seteuid(uint32_t); int setgid(int); +int setpgrp(void); int setpgid(int, int); int setpriority(int, unsigned, int); int setregid(uint32_t, uint32_t); @@ -206,7 +206,7 @@ long ptrace(int, ...); long telldir(DIR *); long times(struct tms *); size_t GetFileSize(const char *); -size_t getfiledescriptorsize(int); +ssize_t getfiledescriptorsize(int); ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t lseek(int, int64_t, unsigned); @@ -233,6 +233,8 @@ void rewinddir(DIR *); void sync(void); int getloadavg(double *, int); int seccomp(unsigned, unsigned, void *); +int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t, + int *); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § system calls » formatting ─╬─│┼ diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index f4d2157e4..9a6f7858c 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -110,6 +110,7 @@ o/$(MODE)/libc/calls/execle.o \ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ +o/$(MODE)/libc/calls/readlinkat-nt.o \ o/$(MODE)/libc/calls/mkntenvblock.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index be94dd304..65c027ce5 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -31,6 +31,7 @@ textwindows int sys_chdir_nt(const char *path) { int e, ms, err, len; char16_t path16[PATH_MAX], var[4]; if ((len = __mkntpath(path, path16)) == -1) return -1; + if (!len) return enoent(); if (len && path16[len - 1] != u'\\') { if (len + 2 > PATH_MAX) return enametoolong(); path16[len + 0] = u'\\'; diff --git a/libc/calls/directmap-nt.c b/libc/calls/directmap-nt.c index 3fa1139ea..78e1f58ee 100644 --- a/libc/calls/directmap-nt.c +++ b/libc/calls/directmap-nt.c @@ -42,16 +42,6 @@ textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot, const struct NtSecurityAttributes *sec; struct NtProcessMemoryCountersEx memcount; -#if _NT_RLIMIT_PWSS_MB - if (GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) { - if (memcount.PeakWorkingSetSize > _NT_RLIMIT_PWSS_MB * 1048576ull) { - kprintf("error: PeakWorkingSetSize %'ldmb exceeded %'ldmb limit%n", - memcount.PeakWorkingSetSize / 1048576, (long)_NT_RLIMIT_PWSS_MB); - _Exit(201); - } - } -#endif - if (fd != -1) { handle = g_fds.p[fd].handle; } else { diff --git a/libc/calls/directmap.c b/libc/calls/directmap.c index 6dcf9f03c..7c8a12d6d 100644 --- a/libc/calls/directmap.c +++ b/libc/calls/directmap.c @@ -22,6 +22,7 @@ #include "libc/intrin/describeflags.internal.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.internal.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/str/str.h" /** diff --git a/libc/calls/dup3-sysv.c b/libc/calls/dup3-sysv.c index c42462def..ec9f64650 100644 --- a/libc/calls/dup3-sysv.c +++ b/libc/calls/dup3-sysv.c @@ -20,15 +20,13 @@ #include "libc/calls/strace.internal.h" #include "libc/errno.h" -#define __NR_dup3_linux 0x0124 /*RHEL5:CVE-2010-3301*/ - int32_t sys_dup3(int32_t oldfd, int32_t newfd, int flags) { static bool once, demodernize; int olderr, fd; if (!once) { olderr = errno; fd = __sys_dup3(oldfd, newfd, flags); - if ((fd == -1 && errno == ENOSYS) || fd == __NR_dup3_linux) { + if (fd == -1 && errno == ENOSYS) { STRACE("demodernizing %s() due to %s", "dup3", "RHEL5:CVE-2010-3301"); demodernize = true; once = true; diff --git a/libc/calls/fixenotdir.c b/libc/calls/fixenotdir.c index 37d6ecb9b..8b982fca4 100644 --- a/libc/calls/fixenotdir.c +++ b/libc/calls/fixenotdir.c @@ -42,6 +42,17 @@ static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) { return false; } +textwindows dontinline int64_t __fix_enotdir3(int64_t rc, char16_t *path1, + char16_t *path2) { + if (rc == -1 && errno == kNtErrorPathNotFound) { + if ((!path1 || !SubpathExistsThatsNotDirectory(path1)) && + (!path2 || !SubpathExistsThatsNotDirectory(path2))) { + errno = kNtErrorFileNotFound; + } + } + return rc; +} + // WIN32 doesn't distinguish between ENOTDIR and ENOENT. UNIX strictly // requires that a directory component *exists* but is not a directory // whereas WIN32 will return ENOTDIR if a dir label simply isn't found @@ -54,10 +65,5 @@ static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) { // dangling symbolic link. // textwindows int64_t __fix_enotdir(int64_t rc, char16_t *path) { - if (rc == -1 && errno == kNtErrorPathNotFound) { - if (!SubpathExistsThatsNotDirectory(path)) { - errno = kNtErrorFileNotFound; - } - } - return rc; + return __fix_enotdir3(rc, path, 0); } diff --git a/libc/calls/fstat.c b/libc/calls/fstat.c index 1b2a7b952..b7d7d46a3 100644 --- a/libc/calls/fstat.c +++ b/libc/calls/fstat.c @@ -27,6 +27,8 @@ /** * Returns information about file, via open()'d descriptor. + * + * @return 0 on success or -1 w/ errno * @asyncsignalsafe */ int fstat(int fd, struct stat *st) { diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 5a77452ca..468752e70 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -39,8 +39,8 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, 0)) != -1) { rc = st ? sys_fstat_nt(fh, st) : 0; CloseHandle(fh); - return rc; } else { - return __winerr(); + rc = __winerr(); } + return __fix_enotdir(rc, path16); } diff --git a/libc/calls/ftruncate-nt.c b/libc/calls/ftruncate-nt.c index e192c7e62..9c1e79288 100644 --- a/libc/calls/ftruncate-nt.c +++ b/libc/calls/ftruncate-nt.c @@ -25,12 +25,10 @@ textwindows int sys_ftruncate_nt(int64_t handle, uint64_t length) { bool32 ok; int64_t tell; tell = -1; - if (SetFilePointerEx(handle, 0, &tell, kNtFileCurrent)) { + if ((ok = SetFilePointerEx(handle, 0, &tell, kNtFileCurrent))) { ok = SetFilePointerEx(handle, length, NULL, kNtFileBegin) && SetEndOfFile(handle); SetFilePointerEx(handle, tell, NULL, kNtFileBegin); - return ok ? 0 : __winerr(); - } else { - return __winerr(); } + return ok ? 0 : __winerr(); } diff --git a/libc/calls/getcwd-xnu.c b/libc/calls/getcwd-xnu.c index 960d56918..63bfac438 100644 --- a/libc/calls/getcwd-xnu.c +++ b/libc/calls/getcwd-xnu.c @@ -29,14 +29,15 @@ char *sys_getcwd_xnu(char *res, size_t size) { int fd; - struct stat st[2]; + union metastat st[2]; char buf[XNU_MAXPATHLEN], *ret = NULL; if ((fd = sys_openat(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) { - if (sys_fstat(fd, &st[0]) != -1) { - if (st[0].st_dev && st[0].st_ino) { + if (__sys_fstat(fd, &st[0]) != -1) { + if (METASTAT(st[0], st_dev) && METASTAT(st[0], st_ino)) { if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) { - if (sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) { - if (st[0].st_dev == st[1].st_dev && st[0].st_ino == st[1].st_ino) { + if (__sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) { + if (METASTAT(st[0], st_dev) == METASTAT(st[1], st_dev) && + METASTAT(st[0], st_ino) == METASTAT(st[1], st_ino)) { if (memccpy(res, buf, '\0', size)) { ret = res; } else { diff --git a/libc/calls/getfiledescriptorsize.c b/libc/calls/getfiledescriptorsize.c index 5fa78a2a9..045e3ecd0 100644 --- a/libc/calls/getfiledescriptorsize.c +++ b/libc/calls/getfiledescriptorsize.c @@ -16,20 +16,65 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/limits.h" +#include "libc/nt/enum/fileinfobyhandleclass.h" #include "libc/nt/files.h" +#include "libc/nt/struct/filestandardinformation.h" +#include "libc/sysv/errfuns.h" +#include "libc/zipos/zipos.internal.h" /** * Determines size of open file. * - * @return file byte length, or -1ul w/ errno + * This function is equivalent to: + * + * struct stat st; + * !fstat(fd, &st) ? st.st_size : -1 + * + * Except faster on BSD/Windows and a much smaller link size. + * + * @return file byte length, or -1 w/ errno * @asyncsignalsafe */ -size_t getfiledescriptorsize(int fd) { - struct stat st; - if (fstat(fd, &st) == -1) return SIZE_MAX; - return st.st_size; +ssize_t getfiledescriptorsize(int fd) { + int e; + ssize_t rc; + union metastat st; + struct NtFileStandardInformation info; + e = errno; + if (__isfdkind(fd, kFdZip)) { + if (weaken(__zipos_fstat)( + (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, &st.cosmo) != + -1) { + rc = st.cosmo.st_size; + } else { + rc = -1; + } + } else if (IsMetal()) { + rc = -1; + } else if (!IsWindows()) { + if (!__sys_fstat(fd, &st)) { + rc = METASTAT(st, st_size); + } else { + rc = -1; + } + } else if (__isfdopen(fd)) { + if (GetFileInformationByHandleEx(g_fds.p[fd].handle, kNtFileStandardInfo, + &info, sizeof(info))) { + rc = info.EndOfFile; + } else { + rc = ebadf(); + } + } else { + rc = ebadf(); + } + STRACE("getfiledescriptorsize(%d) → %'zd% m", fd, rc); + return rc; } diff --git a/libc/calls/getfilesize.c b/libc/calls/getfilesize.c index cfd7c8f1c..8ef96630c 100644 --- a/libc/calls/getfilesize.c +++ b/libc/calls/getfilesize.c @@ -28,7 +28,7 @@ * Returns the byte length of file by path. * * @return number of bytes, or -1ul w/ errno - * @see getfiledescriptorsize + * @see getfiledescriptorsize() */ size_t GetFileSize(const char *pathname) { struct stat st; diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 16d3f64c9..713383060 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -131,6 +131,7 @@ i32 __sys_getrusage(i32, struct rusage *) hidden; i32 __sys_munmap(void *, u64) hidden; i32 __sys_openat(i32, const char *, i32, u32) hidden; i32 __sys_pipe2(i32[hasatleast 2], u32) hidden; +i32 __sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; i32 __sys_wait4(i32, i32 *, i32, struct rusage *) hidden; i32 sys_chdir(const char *) hidden; @@ -197,7 +198,7 @@ i32 sys_setsid(void) hidden; i32 sys_setuid(i32) hidden; i32 sys_sigaction(i32, const void *, void *, i64, i64) hidden; i32 sys_sigaltstack(const void *, void *) hidden; -i32 sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; +i32 sys_sigprocmask(i32, const sigset *, sigset *) hidden; i32 sys_sigqueue(i32, i32, const union sigval) hidden; i32 sys_sigqueueinfo(i32, const siginfo_t *) hidden; i32 sys_sigsuspend(const sigset *, u64) hidden; @@ -326,6 +327,7 @@ int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden; ╚────────────────────────────────────────────────────────────────────────────│*/ int64_t __fix_enotdir(int64_t, char16_t *) hidden; +int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *) hidden; bool _check_interrupts(bool, struct Fd *) hidden; void _check_sigchld(void) hidden; void _check_sigalrm(void) hidden; diff --git a/libc/calls/linkat-nt.c b/libc/calls/linkat-nt.c index c1e4bfae4..072c203f6 100644 --- a/libc/calls/linkat-nt.c +++ b/libc/calls/linkat-nt.c @@ -30,7 +30,7 @@ textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd, if (CreateHardLink(newpath16, oldpath16, NULL)) { return 0; } else { - return __winerr(); + return __fix_enotdir3(__winerr(), newpath16, oldpath16); } } else { return -1; diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 7dc8f49b7..0bd1e4e12 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -29,6 +29,7 @@ int __mkntpathat(int dirfd, const char *path, int flags, char16_t dir[PATH_MAX]; uint32_t dirlen, filelen; if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; + if (!filelen) return enoent(); if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */ if (!__isfdkind(dirfd, kFdFile)) return ebadf(); dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), diff --git a/libc/calls/munmap-sysv.c b/libc/calls/munmap-sysv.c index db622bd1b..d2ea45e00 100644 --- a/libc/calls/munmap-sysv.c +++ b/libc/calls/munmap-sysv.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/runtime/directmap.internal.h" +#include "libc/runtime/memtrack.internal.h" /** * Unmaps memory directly with system. diff --git a/libc/calls/ntcontext2linux.c b/libc/calls/ntcontext2linux.c index ed7462f54..c1a509b1e 100644 --- a/libc/calls/ntcontext2linux.c +++ b/libc/calls/ntcontext2linux.c @@ -24,7 +24,7 @@ textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { if (!cr) return; ctx->uc_flags = cr->EFlags; - ctx->uc_mcontext.gregs[REG_EFL] = cr->EFlags; + ctx->uc_mcontext.eflags = cr->EFlags; ctx->uc_mcontext.rax = cr->Rax; ctx->uc_mcontext.rbx = cr->Rbx; ctx->uc_mcontext.rcx = cr->Rcx; @@ -52,7 +52,7 @@ textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) { if (!cr) return; cr->EFlags = ctx->uc_flags; - cr->EFlags = ctx->uc_mcontext.gregs[REG_EFL]; + cr->EFlags = ctx->uc_mcontext.eflags; cr->Rax = ctx->uc_mcontext.rax; cr->Rbx = ctx->uc_mcontext.rbx; cr->Rcx = ctx->uc_mcontext.rcx; diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 0e978f0ee..d5f5055bb 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -93,5 +93,5 @@ textwindows int ntspawn( } if (block) UnmapViewOfFile(block); if (handle) CloseHandle(handle); - return rc; + return __fix_enotdir(rc, prog16); } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index ad967c610..7728acdcd 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -31,7 +31,6 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { int64_t hin, hout; int reader, writer; char16_t pipename[64]; - if (!pipefd) return efault(); CreatePipeName(pipename); if ((reader = __reservefd(-1)) == -1) return -1; if ((writer = __reservefd(-1)) == -1) { diff --git a/libc/calls/pipe.c b/libc/calls/pipe.c index e7ec101c0..bbced47d4 100644 --- a/libc/calls/pipe.c +++ b/libc/calls/pipe.c @@ -28,12 +28,16 @@ * * @param fd is (reader, writer) * @return 0 on success or -1 w/ errno + * @raise EFAULT if pipefd is NULL or an invalid address + * @raise EMFILE if RLIMIT_NOFILE is exceedde * @asyncsignalsafe * @see pipe2() */ int pipe(int pipefd[hasatleast 2]) { int rc; - if (IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2)) { + if (!pipefd || (IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2))) { + // needed for windows which is polyfilled + // needed for xnu and netbsd which don't take an argument rc = efault(); } else if (!IsWindows()) { rc = sys_pipe(pipefd); diff --git a/libc/calls/pipe2-sysv.c b/libc/calls/pipe2-sysv.c index ab75113a1..34cb734ae 100644 --- a/libc/calls/pipe2-sysv.c +++ b/libc/calls/pipe2-sysv.c @@ -19,16 +19,15 @@ #include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/errno.h" - -#define __NR_pipe2_linux 0x0125 /*RHEL5:CVE-2010-3301*/ +#include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" int32_t sys_pipe2(int pipefd[hasatleast 2], unsigned flags) { int rc, olderr; if (!flags) goto OldSkool; olderr = errno; rc = __sys_pipe2(pipefd, flags); - if ((rc == -1 && errno == ENOSYS) || - (SupportsLinux() && rc == __NR_pipe2_linux)) { + if (rc == -1 && errno == ENOSYS) { errno = olderr; OldSkool: if ((rc = sys_pipe(pipefd)) != -1) { diff --git a/libc/calls/pipe2.c b/libc/calls/pipe2.c index faa8e4d3e..2c916775a 100644 --- a/libc/calls/pipe2.c +++ b/libc/calls/pipe2.c @@ -20,20 +20,22 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" /** * Creates file-less file descriptors for interprocess communication. * * @param pipefd is used to return (reader, writer) file descriptors - * @param flags can have O_CLOEXEC, O_NONBLOCK, O_DIRECT + * @param flags can have O_CLOEXEC or O_DIRECT or O_NONBLOCK * @return 0 on success, or -1 w/ errno and pipefd isn't modified */ int pipe2(int pipefd[hasatleast 2], int flags) { int rc; - if (!pipefd) { - rc = efault(); - } else if (IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2)) { + if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT)) { + return einval(); + } else if (!pipefd || + (IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2))) { rc = efault(); } else if (!IsWindows()) { rc = sys_pipe2(pipefd, flags); diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index f503be71c..dd0784083 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -30,8 +30,6 @@ #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" -#define __NR_preadv_linux 0x0127 - /** * Reads with maximum generality. * @@ -76,14 +74,6 @@ ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { once = true; demodernize = true; STRACE("demodernizing %s() due to %s", "preadv", "ENOSYS"); - } else if (IsLinux() && rc == __NR_preadv_linux) { - if (__iovec_size(iov, iovlen) < __NR_preadv_linux) { - demodernize = true; - STRACE("demodernizing %s() due to %s", "preadv", "RHEL5:CVE-2010-3301"); - once = true; - } else { - return rc; - } } else { once = true; return rc; diff --git a/libc/calls/program_executable_name.c b/libc/calls/program_executable_name.c index 8845cac64..0ef78b22f 100644 --- a/libc/calls/program_executable_name.c +++ b/libc/calls/program_executable_name.c @@ -24,6 +24,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/mem/alloca.h" @@ -46,7 +47,7 @@ char program_executable_name[SIZE]; -static textwindows bool GetNtExePath(char executable[SIZE]) { +static textwindows bool GetNtExePath(char exe[SIZE]) { bool32 rc; uint64_t w; wint_t x, y; @@ -54,7 +55,7 @@ static textwindows bool GetNtExePath(char executable[SIZE]) { char16_t path16[PATH_MAX + 1]; path16[0] = 0; rc = GetModuleFileName(0, path16, ARRAYLEN(path16)); - STRACE("GetModuleFileName(0, [%#hs]) → %hhhd", path16, rc); + NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", path16, rc); if (!rc) return false; for (i = j = 0; (x = path16[i++] & 0xffff);) { if (!IsUcs2(x)) { @@ -64,47 +65,49 @@ static textwindows bool GetNtExePath(char executable[SIZE]) { if (x == '\\') x = '/'; w = tpenc(x); do { - executable[j] = w; + exe[j] = w; if (++j == SIZE) { return false; } } while ((w >>= 8)); } - executable[j] = 0; + exe[j] = 0; return true; } -static void ReadProgramExecutableName(char executable[SIZE], char *argv0, +static void ReadProgramExecutableName(char exe[SIZE], char *argv0, uintptr_t *auxv) { + int e; size_t m; ssize_t n; int cmd[4]; char *p, *t; - if (IsWindows() && GetNtExePath(executable)) { - return; - } - for (p = 0; *auxv; auxv += 2) { - if (*auxv == AT_EXECFN) { - p = (char *)auxv[1]; - break; - } - } - n = 0; - if (!p) p = argv0; - if (p) { - if (!_isabspath(p)) { - if (getcwd(executable, SIZE - 1)) { - n = strlen(executable); - executable[n++] = '/'; + e = errno; + if (!IsWindows() || !GetNtExePath(exe)) { + for (p = 0; *auxv; auxv += 2) { + if (*auxv == AT_EXECFN) { + p = (char *)auxv[1]; + break; } } - for (; *p; ++p) { - if (n + 1 < SIZE) { - executable[n++] = *p; + n = 0; + if (!p) p = argv0; + if (p) { + if (!_isabspath(p)) { + if (getcwd(exe, SIZE - 1)) { + n = strlen(exe); + exe[n++] = '/'; + } + } + for (; *p; ++p) { + if (n + 1 < SIZE) { + exe[n++] = *p; + } } } + exe[n] = 0; } - executable[n] = 0; + errno = e; } /** @@ -124,14 +127,9 @@ static void ReadProgramExecutableName(char executable[SIZE], char *argv0, * @see program_invocation_name */ char *GetProgramExecutableName(void) { - int e; static bool once; - char executable[SIZE]; if (!once) { - e = errno; - ReadProgramExecutableName(executable, __argv[0], __auxv); - errno = e; - __stpcpy(program_executable_name, executable); + ReadProgramExecutableName(program_executable_name, __argv[0], __auxv); once = true; } return program_executable_name; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index db15e7746..4b940eb45 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -29,8 +29,6 @@ #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" -#define __NR_pwritev_linux 0x0128 - /** * Writes data from multiple buffers to offset. * @@ -80,15 +78,6 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { once = true; demodernize = true; STRACE("demodernizing %s() due to %s", "pwritev", "ENOSYS"); - } else if (IsLinux() && rc == __NR_pwritev_linux) { - if (__iovec_size(iov, iovlen) < __NR_pwritev_linux) { - demodernize = true; - STRACE("demodernizing %s() due to %s", "pwritev", - "RHEL5:CVE-2010-3301"); - once = true; - } else { - return rc; - } } else { once = true; return rc; diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index ed64fa32f..9c2d44692 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -16,30 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/bits.h" -#include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" -#include "libc/calls/struct/iovec.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/limits.h" #include "libc/nt/enum/filetype.h" -#include "libc/nt/enum/wait.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/ipc.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/synchronization.h" -#include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, size_t size, ssize_t offset) { - uint32_t err, got, avail; + uint32_t got, avail; struct NtOverlapped overlap; if (GetFileType(fd->handle) == kNtFileTypePipe) { for (;;) { @@ -60,14 +51,16 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, _offset2overlap(fd->handle, offset, &overlap))) { return got; } - err = GetLastError(); - // make sure read() returns 0 on broken pipe - if (err == kNtErrorBrokenPipe) return 0; - // make sure read() returns 0 on closing named pipe - if (err == kNtErrorNoData) return 0; - // make sure pread() returns 0 if we start reading after EOF - if (err == kNtErrorHandleEof) return 0; - return __winerr(); + switch (GetLastError()) { + case kNtErrorBrokenPipe: // broken pipe + case kNtErrorNoData: // closing named pipe + case kNtErrorHandleEof: // pread read past EOF + return 0; // + case kNtErrorAccessDenied: // read doesn't return EACCESS + return ebadf(); // + default: + return __winerr(); + } } textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov, diff --git a/libc/calls/readlink.c b/libc/calls/readlink.c index 67d6adeab..d758647f5 100644 --- a/libc/calls/readlink.c +++ b/libc/calls/readlink.c @@ -22,16 +22,10 @@ /** * Reads symbolic link. * - * This does *not* nul-terminate the buffer. - * - * It is recommended that malloc() be linked into your program when - * using this function. Otherwise the buffer should be larger. It should - * also be noted that, without malloc, long names with many astral plane - * characters might not decode properly. - * * @param path must be a symbolic link pathname * @param buf will receive symbolic link contents, and won't be modified * unless the function succeeds (with the exception of no-malloc nt) + * and this buffer will *not* be nul-terminated * @return number of bytes written to buf, or -1 w/ errno; if the * return is equal to bufsiz then truncation may have occurred * @see readlinkat(AT_FDCWD, ...) for modern version of this diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index af1cdc3a1..bc9ac7048 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -16,22 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/bits.h" -#include "libc/bits/weaken.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/mem/mem.h" -#include "libc/nexgen32e/bsr.h" +#include "libc/mem/alloca.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/creationdisposition.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/fsctl.h" #include "libc/nt/enum/io.h" -#include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/reparsedatabuffer.h" @@ -45,25 +37,15 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, ssize_t rc; uint64_t w; wint_t x, y; - void *freeme; + volatile char *memory; uint32_t i, j, n, mem; char16_t path16[PATH_MAX], *p; struct NtReparseDataBuffer *rdb; - if (__mkntpathat(dirfd, path, 0, path16) == -1) { - return -1; - } - if (weaken(malloc)) { - mem = 16384; - rdb = weaken(malloc)(mem); - freeme = rdb; - } else if (bufsiz >= sizeof(struct NtReparseDataBuffer) + 16) { - mem = bufsiz; - rdb = (struct NtReparseDataBuffer *)buf; - freeme = 0; - } else { - NTTRACE("sys_readlinkat_nt() needs bigger buffer malloc() to be yoinked"); - return enomem(); - } + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; + mem = 16384; + memory = alloca(mem); + for (i = 0; i < mem; i += PAGESIZE) memory[i] = 0; + rdb = (struct NtReparseDataBuffer *)memory; if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting, kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics, 0)) != -1) { @@ -99,26 +81,17 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, w >>= 8; } while (w); } - if (freeme || (intptr_t)(buf + j) <= (intptr_t)(p + i)) { - rc = j; - } else { - NTTRACE("sys_readlinkat_nt() too many astral codepoints"); - rc = enametoolong(); - } + rc = j; } else { NTTRACE("sys_readlinkat_nt() should have kNtIoReparseTagSymlink"); rc = einval(); } } else { - assert(errno == EINVAL); rc = -1; } CloseHandle(h); } else { - rc = -1; - } - if (freeme && weaken(free)) { - weaken(free)(freeme); + rc = __fix_enotdir(-1, path16); } return rc; } diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index 374213175..58da8789e 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -17,14 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -33,16 +29,12 @@ * * This does *not* nul-terminate the buffer. * - * It is recommended that malloc() be linked into your program when - * using this function. Otherwise the buffer should be larger. It should - * also be noted that, without malloc, long names with many astral plane - * characters might not decode properly. - * * @param dirfd is normally AT_FDCWD but if it's an open directory and * file is a relative path, then file is opened relative to dirfd * @param path must be a symbolic link pathname * @param buf will receive symbolic link contents, and won't be modified * unless the function succeeds (with the exception of no-malloc nt) + * and this buffer will *not* be nul-terminated * @return number of bytes written to buf, or -1 w/ errno; if the * return is equal to bufsiz then truncation may have occurred * @error EINVAL if path isn't a symbolic link diff --git a/libc/calls/rename.c b/libc/calls/rename.c index f01f42ebc..8537d0502 100644 --- a/libc/calls/rename.c +++ b/libc/calls/rename.c @@ -22,6 +22,12 @@ /** * Moves file the Unix way. * + * This is generally an atomic operation with the file system, since all + * it's doing is changing a name associated with an inode. However, that + * means rename() doesn't permit your `oldpathname` and `newpathname` to + * be on separate file systems, in which case this returns EXDEV. That's + * also the case on Windows. + * * @return 0 on success or -1 w/ errno * @asyncsignalsafe */ diff --git a/libc/calls/renameat-nt.c b/libc/calls/renameat-nt.c index 5f0940d83..949d05d84 100644 --- a/libc/calls/renameat-nt.c +++ b/libc/calls/renameat-nt.c @@ -19,12 +19,9 @@ #include "libc/calls/internal.h" #include "libc/nt/enum/movefileexflags.h" #include "libc/nt/files.h" -#include "libc/nt/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/errfuns.h" textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, - const char *newpath) { + const char *newpath) { char16_t oldpath16[PATH_MAX]; char16_t newpath16[PATH_MAX]; if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || @@ -34,6 +31,6 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { return 0; } else { - return __winerr(); + return __fix_enotdir3(-1, oldpath16, newpath16); } } diff --git a/libc/calls/renameat.c b/libc/calls/renameat.c index f2cd5087f..71bde23d6 100644 --- a/libc/calls/renameat.c +++ b/libc/calls/renameat.c @@ -29,6 +29,12 @@ /** * Renames files relative to directories. * + * This is generally an atomic operation with the file system, since all + * it's doing is changing a name associated with an inode. However, that + * means rename() doesn't permit your `oldpathname` and `newpathname` to + * be on separate file systems, in which case this returns EXDEV. That's + * also the case on Windows. + * * @param olddirfd is normally AT_FDCWD but if it's an open directory * and oldpath is relative, then oldpath become relative to dirfd * @param newdirfd is normally AT_FDCWD but if it's an open directory diff --git a/libc/calls/setpgrp.c b/libc/calls/setpgrp.c new file mode 100644 index 000000000..e7c549f27 --- /dev/null +++ b/libc/calls/setpgrp.c @@ -0,0 +1,26 @@ +/*-*- 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" + +/** + * Sets the process group ID. + */ +int setpgrp(void) { + return setpgid(0, 0); +} diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c index 77127136f..5ef298940 100644 --- a/libc/calls/setrlimit.c +++ b/libc/calls/setrlimit.c @@ -44,11 +44,20 @@ * when the hard limit is exceeded. It works everywhere but Windows * * - `RLIMIT_NPROC` limits the number of simultaneous processes and it - * should work on all platforms except Windows. + * should work on all platforms except Windows. Please be advised it + * limits the process, with respect to the activities of the user id + * as a whole. * * - `RLIMIT_NOFILE` limits the number of open file descriptors and it * should work on all platforms except Windows (TODO) * + * The rlimit magnums differ for each platform but occupy the interval + * zero through `RLIM_NLIMITS`. Usually they're set to `RLIM_INFINITY` + * which is `-1` on Linux/Windows, and `LONG_MAX` on BSDs. In any case + * they're both very large numbers under the Cosmopolitan unsigned ABI + * because struct rlimit uses uint64_t. The special magnum 127 is used + * for constant values that aren't supported by the host platform. + * * @param rlim specifies new resource limit * @return 0 on success or -1 w/ errno * @see libc/sysv/consts.sh diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index fe380e8cc..e48b5fc29 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -149,7 +149,11 @@ static textwindows bool __sig_deliver(bool restartable, int sig, int si_code, * Returns true if signal default action is to end process. */ static textwindows bool __sig_isfatal(int sig) { - return sig != SIGCHLD; + if (sig == SIGCHLD || sig == SIGURG || sig == SIGWINCH) { + return false; + } else { + return true; + } } /** diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index df89c892e..093b56f00 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -42,6 +42,8 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +#undef sigaction + #ifdef SYSDEBUG STATIC_YOINK("strsignal"); // for kprintf() #endif @@ -75,9 +77,9 @@ union metasigaction { struct sigaction_xnu_out xnu_out; }; -void __sigenter_netbsd(int, void *, void *); -void __sigenter_freebsd(int, void *, void *); -void __sigenter_openbsd(int, void *, void *); +void __sigenter_netbsd(int, void *, void *) hidden; +void __sigenter_freebsd(int, void *, void *) hidden; +void __sigenter_openbsd(int, void *, void *) hidden; static void sigaction_cosmo2native(union metasigaction *sa) { if (!sa) return; @@ -279,7 +281,7 @@ static int __sigaction(int sig, const struct sigaction *act, * `SA_NOMASK` for this flag, which means the same thing. * * - `SA_NOCLDWAIT`: Changes `SIGCHLD` so the zombie is gone and you - * can't call `wait()` anymore; similar to SIGCHLD + SIG_IGN but may + * can't call `wait()` anymore; similar but may * still deliver the SIGCHLD. * * - `SA_NOCLDSTOP`: Lets you set `SIGCHLD` handler that's only notified @@ -435,7 +437,7 @@ static int __sigaction(int sig, const struct sigaction *act, * @asyncsignalsafe * @vforksafe */ -int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { +int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { int rc; char buf[2][128]; if (sig == SIGKILL || sig == SIGSTOP) { diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c index 1807967a0..6d8b7a073 100644 --- a/libc/calls/sigchld-nt.c +++ b/libc/calls/sigchld-nt.c @@ -45,10 +45,9 @@ void _check_sigchld(void) { STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError()); return; } - if (__sighandrvas[SIGCHLD] == (intptr_t)SIG_IGN) { - STRACE("killing zombie fd=%d handle=%ld", pids[i], handles[i]); - CloseHandle(handles[i]); - __releasefd(pids[i]); + if (__sighandrvas[SIGCHLD] == (intptr_t)SIG_IGN || + __sighandrvas[SIGCHLD] == (intptr_t)SIG_DFL) { + STRACE("new zombie fd=%d handle=%ld", pids[i], handles[i]); return; } if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) { diff --git a/libc/calls/sigenter-netbsd.c b/libc/calls/sigenter-netbsd.c index e3ee74511..0d2595a96 100644 --- a/libc/calls/sigenter-netbsd.c +++ b/libc/calls/sigenter-netbsd.c @@ -21,68 +21,13 @@ #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/siginfo-netbsd.internal.h" #include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -#define RDI 0 -#define RSI 1 -#define RDX 2 -#define R10 6 -#define R8 4 -#define R9 5 -#define RCX 3 -#define R11 7 -#define R12 8 -#define R13 9 -#define R14 10 -#define R15 11 -#define RBP 12 -#define RBX 13 -#define RAX 14 -#define GS 15 -#define FS 16 -#define ES 17 -#define DS 18 -#define TRAP 19 -#define ERR 20 -#define RIP 21 -#define CS 22 -#define RFLAGS 23 -#define RSP 24 -#define SS 25 - -union sigval_netbsd { - int32_t sival_int; - void *sival_ptr; -}; - -struct sigset_netbsd { - uint32_t __bits[4]; -}; - -struct stack_netbsd { - void *ss_sp; - size_t ss_size; - int32_t ss_flags; -}; - -struct mcontext_netbsd { - int64_t __gregs[26]; - int64_t _mc_tlsbase; - struct FpuState __fpregs; -}; - -struct ucontext_netbsd { - uint32_t uc_flags; - struct ucontext_netbsd *uc_link; - struct sigset_netbsd uc_sigmask; - struct stack_netbsd uc_stack; - struct mcontext_netbsd uc_mcontext; -}; - void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, struct ucontext_netbsd *ctx) { int rva, flags; @@ -109,50 +54,50 @@ void __sigenter_netbsd(int sig, struct siginfo_netbsd *si, uc.uc_stack.ss_flags = ctx->uc_stack.ss_flags; memcpy(&uc.uc_sigmask, &ctx->uc_sigmask, MIN(sizeof(uc.uc_sigmask), sizeof(ctx->uc_sigmask))); - uc.uc_mcontext.rdi = ctx->uc_mcontext.__gregs[RDI]; - uc.uc_mcontext.rsi = ctx->uc_mcontext.__gregs[RSI]; - uc.uc_mcontext.rdx = ctx->uc_mcontext.__gregs[RDX]; - uc.uc_mcontext.rcx = ctx->uc_mcontext.__gregs[RCX]; - uc.uc_mcontext.r8 = ctx->uc_mcontext.__gregs[R8]; - uc.uc_mcontext.r9 = ctx->uc_mcontext.__gregs[R9]; - uc.uc_mcontext.rax = ctx->uc_mcontext.__gregs[RAX]; - uc.uc_mcontext.rbx = ctx->uc_mcontext.__gregs[RBX]; - uc.uc_mcontext.rbp = ctx->uc_mcontext.__gregs[RBP]; - uc.uc_mcontext.r10 = ctx->uc_mcontext.__gregs[R10]; - uc.uc_mcontext.r11 = ctx->uc_mcontext.__gregs[R11]; - uc.uc_mcontext.r12 = ctx->uc_mcontext.__gregs[R12]; - uc.uc_mcontext.r13 = ctx->uc_mcontext.__gregs[R13]; - uc.uc_mcontext.r14 = ctx->uc_mcontext.__gregs[R14]; - uc.uc_mcontext.r15 = ctx->uc_mcontext.__gregs[R15]; - uc.uc_mcontext.trapno = ctx->uc_mcontext.__gregs[TRAP]; - uc.uc_mcontext.fs = ctx->uc_mcontext.__gregs[FS]; - uc.uc_mcontext.gs = ctx->uc_mcontext.__gregs[GS]; - uc.uc_mcontext.err = ctx->uc_mcontext.__gregs[ERR]; - uc.uc_mcontext.rip = ctx->uc_mcontext.__gregs[RIP]; - uc.uc_mcontext.rsp = ctx->uc_mcontext.__gregs[RSP]; + uc.uc_mcontext.rdi = ctx->uc_mcontext.rdi; + uc.uc_mcontext.rsi = ctx->uc_mcontext.rsi; + uc.uc_mcontext.rdx = ctx->uc_mcontext.rdx; + uc.uc_mcontext.rcx = ctx->uc_mcontext.rcx; + uc.uc_mcontext.r8 = ctx->uc_mcontext.r8; + uc.uc_mcontext.r9 = ctx->uc_mcontext.r9; + uc.uc_mcontext.rax = ctx->uc_mcontext.rax; + uc.uc_mcontext.rbx = ctx->uc_mcontext.rbx; + uc.uc_mcontext.rbp = ctx->uc_mcontext.rbp; + uc.uc_mcontext.r10 = ctx->uc_mcontext.r10; + uc.uc_mcontext.r11 = ctx->uc_mcontext.r11; + uc.uc_mcontext.r12 = ctx->uc_mcontext.r12; + uc.uc_mcontext.r13 = ctx->uc_mcontext.r13; + uc.uc_mcontext.r14 = ctx->uc_mcontext.r14; + uc.uc_mcontext.r15 = ctx->uc_mcontext.r15; + uc.uc_mcontext.trapno = ctx->uc_mcontext.trapno; + uc.uc_mcontext.fs = ctx->uc_mcontext.fs; + uc.uc_mcontext.gs = ctx->uc_mcontext.gs; + uc.uc_mcontext.err = ctx->uc_mcontext.err; + uc.uc_mcontext.rip = ctx->uc_mcontext.rip; + uc.uc_mcontext.rsp = ctx->uc_mcontext.rsp; *uc.uc_mcontext.fpregs = ctx->uc_mcontext.__fpregs; ((sigaction_f)(_base + rva))(sig, &si2, &uc); - ctx->uc_mcontext.__gregs[RDI] = uc.uc_mcontext.rdi; - ctx->uc_mcontext.__gregs[RSI] = uc.uc_mcontext.rsi; - ctx->uc_mcontext.__gregs[RDX] = uc.uc_mcontext.rdx; - ctx->uc_mcontext.__gregs[RCX] = uc.uc_mcontext.rcx; - ctx->uc_mcontext.__gregs[R8] = uc.uc_mcontext.r8; - ctx->uc_mcontext.__gregs[R9] = uc.uc_mcontext.r9; - ctx->uc_mcontext.__gregs[RAX] = uc.uc_mcontext.rax; - ctx->uc_mcontext.__gregs[RBX] = uc.uc_mcontext.rbx; - ctx->uc_mcontext.__gregs[RBP] = uc.uc_mcontext.rbp; - ctx->uc_mcontext.__gregs[R10] = uc.uc_mcontext.r10; - ctx->uc_mcontext.__gregs[R11] = uc.uc_mcontext.r11; - ctx->uc_mcontext.__gregs[R12] = uc.uc_mcontext.r12; - ctx->uc_mcontext.__gregs[R13] = uc.uc_mcontext.r13; - ctx->uc_mcontext.__gregs[R14] = uc.uc_mcontext.r14; - ctx->uc_mcontext.__gregs[R15] = uc.uc_mcontext.r15; - ctx->uc_mcontext.__gregs[TRAP] = uc.uc_mcontext.trapno; - ctx->uc_mcontext.__gregs[FS] = uc.uc_mcontext.fs; - ctx->uc_mcontext.__gregs[GS] = uc.uc_mcontext.gs; - ctx->uc_mcontext.__gregs[ERR] = uc.uc_mcontext.err; - ctx->uc_mcontext.__gregs[RIP] = uc.uc_mcontext.rip; - ctx->uc_mcontext.__gregs[RSP] = uc.uc_mcontext.rsp; + ctx->uc_mcontext.rdi = uc.uc_mcontext.rdi; + ctx->uc_mcontext.rsi = uc.uc_mcontext.rsi; + ctx->uc_mcontext.rdx = uc.uc_mcontext.rdx; + ctx->uc_mcontext.rcx = uc.uc_mcontext.rcx; + ctx->uc_mcontext.r8 = uc.uc_mcontext.r8; + ctx->uc_mcontext.r9 = uc.uc_mcontext.r9; + ctx->uc_mcontext.rax = uc.uc_mcontext.rax; + ctx->uc_mcontext.rbx = uc.uc_mcontext.rbx; + ctx->uc_mcontext.rbp = uc.uc_mcontext.rbp; + ctx->uc_mcontext.r10 = uc.uc_mcontext.r10; + ctx->uc_mcontext.r11 = uc.uc_mcontext.r11; + ctx->uc_mcontext.r12 = uc.uc_mcontext.r12; + ctx->uc_mcontext.r13 = uc.uc_mcontext.r13; + ctx->uc_mcontext.r14 = uc.uc_mcontext.r14; + ctx->uc_mcontext.r15 = uc.uc_mcontext.r15; + ctx->uc_mcontext.trapno = uc.uc_mcontext.trapno; + ctx->uc_mcontext.fs = uc.uc_mcontext.fs; + ctx->uc_mcontext.gs = uc.uc_mcontext.gs; + ctx->uc_mcontext.err = uc.uc_mcontext.err; + ctx->uc_mcontext.rip = uc.uc_mcontext.rip; + ctx->uc_mcontext.rsp = uc.uc_mcontext.rsp; ctx->uc_mcontext.__fpregs = *uc.uc_mcontext.fpregs; } } diff --git a/libc/calls/sigprocmask-sysv.c b/libc/calls/sigprocmask-sysv.c new file mode 100644 index 000000000..7f6ce2b41 --- /dev/null +++ b/libc/calls/sigprocmask-sysv.c @@ -0,0 +1,48 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" + +int sys_sigprocmask(int how, const sigset_t *opt_set, + sigset_t *opt_out_oldset) { + int res, rc, arg1; + sigset_t old = {0}; + const sigset_t *arg2; + if (!IsOpenbsd()) { + rc = __sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0, 8); + } else { + if (opt_set) { + // openbsd only supports 32 signals so it passses them in a reg + arg1 = how; + arg2 = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set); + } else { + arg1 = how; // SIG_BLOCK + arg2 = 0; // changes nothing + } + if ((res = __sys_sigprocmask(arg1, arg2, 0, 0)) != -1) { + memcpy(&old, &res, sizeof(res)); + rc = 0; + } else { + rc = -1; + } + } + if (rc != -1 && opt_out_oldset) { + *opt_out_oldset = old; + } + return rc; +} diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index d02c21ce6..f34fef059 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -68,26 +68,11 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) { (opt_out_oldset && !__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset))))) { rc = efault(); - } else if (IsLinux() || IsXnu() || IsFreebsd() || IsNetbsd()) { - rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0, 8); - } else if (IsOpenbsd()) { - if (opt_set) { - // openbsd only supports 32 signals so it passses them in a reg - arg1 = how; - arg2 = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set); - } else { - arg1 = how; // SIG_BLOCK - arg2 = 0; // changes nothing - } - if ((res = sys_sigprocmask(arg1, arg2, 0, 0)) != -1) { - memcpy(&old, &res, sizeof(res)); - rc = 0; - } else { - rc = -1; - } - } else { // windows or metal + } else if (IsMetal() || IsWindows()) { rc = __sig_mask(how, opt_set, &old); _check_interrupts(false, 0); + } else { + rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0); } if (rc != -1 && opt_out_oldset) { *opt_out_oldset = old; diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 222967edc..14479d578 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -5,11 +5,10 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" -#define _NT_RLIMIT_PWSS_MB 1000 /* nocommit */ -#define _KERNTRACE 0 /* not configurable w/ flag yet */ -#define _POLLTRACE 0 /* not configurable w/ flag yet */ -#define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _KERNTRACE 0 /* not configurable w/ flag yet */ +#define _POLLTRACE 0 /* not configurable w/ flag yet */ +#define _DATATRACE 1 /* not configurable w/ flag yet */ +#define _NTTRACE 0 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " diff --git a/libc/calls/strace_rlimit.c b/libc/calls/strace_rlimit.c index 58b4aea71..b05f60f77 100644 --- a/libc/calls/strace_rlimit.c +++ b/libc/calls/strace_rlimit.c @@ -24,9 +24,27 @@ const char *__strace_rlimit_name(int resource) { static char buf[12]; - if (resource == RLIMIT_AS) return "RLIMIT_AS"; - if (resource == RLIMIT_CPU) return "RLIMIT_CPU"; - if (resource == RLIMIT_FSIZE) return "RLIMIT_FSIZE"; + 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; } @@ -35,6 +53,6 @@ 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, "{%'lu, %'lu}", rlim->rlim_cur, rlim->rlim_max); + ksnprintf(buf, bufsize, "{%'ld, %'ld}", rlim->rlim_cur, rlim->rlim_max); return buf; } diff --git a/libc/calls/struct/bpf.h b/libc/calls/struct/bpf.h index 56355628d..1dc96c763 100644 --- a/libc/calls/struct/bpf.h +++ b/libc/calls/struct/bpf.h @@ -325,6 +325,9 @@ COSMOPOLITAN_C_START_ #define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff #define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56 +#define BPF_F_ADJ_ROOM_ENCAP_L2(len) \ + (((uint64_t)len & BPF_ADJ_ROOM_ENCAP_L2_MASK) << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) + #define BPF_F_SYSCTL_BASE_NAME (1ULL << 0) #define BPF_LOCAL_STORAGE_GET_F_CREATE (1ULL << 0) #define BPF_SK_STORAGE_GET_F_CREATE BPF_LOCAL_STORAGE_GET_F_CREATE @@ -352,7 +355,8 @@ COSMOPOLITAN_C_START_ #define BPF_F_BROADCAST (1ULL << 3) #define BPF_F_EXCLUDE_INGRESS (1ULL << 4) -#define BPF_TAG_SIZE 8 +#define XDP_PACKET_HEADROOM 256 +#define BPF_TAG_SIZE 8 #define BPF_SOCK_OPS_RTO_CB_FLAG (1 << 0) #define BPF_SOCK_OPS_RETRANS_CB_FLAG (1 << 1) @@ -451,10 +455,176 @@ COSMOPOLITAN_C_START_ #define BPF_REDIRECT 7 #define BPF_LWT_REROUTE 128 -#define XDP_PACKET_HEADROOM 256 - -#define BPF_F_ADJ_ROOM_ENCAP_L2(len) \ - (((uint64_t)len & BPF_ADJ_ROOM_ENCAP_L2_MASK) << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) +#define BPF_FUNC_unspec 0 +#define BPF_FUNC_map_lookup_elem 1 +#define BPF_FUNC_map_update_elem 2 +#define BPF_FUNC_map_delete_elem 3 +#define BPF_FUNC_probe_read 4 +#define BPF_FUNC_ktime_get_ns 5 +#define BPF_FUNC_trace_printk 6 +#define BPF_FUNC_get_prandom_u32 7 +#define BPF_FUNC_get_smp_processor_id 8 +#define BPF_FUNC_skb_store_bytes 9 +#define BPF_FUNC_l3_csum_replace 10 +#define BPF_FUNC_l4_csum_replace 11 +#define BPF_FUNC_tail_call 12 +#define BPF_FUNC_clone_redirect 13 +#define BPF_FUNC_get_current_pid_tgid 14 +#define BPF_FUNC_get_current_uid_gid 15 +#define BPF_FUNC_get_current_comm 16 +#define BPF_FUNC_get_cgroup_classid 17 +#define BPF_FUNC_skb_vlan_push 18 +#define BPF_FUNC_skb_vlan_pop 19 +#define BPF_FUNC_skb_get_tunnel_key 20 +#define BPF_FUNC_skb_set_tunnel_key 21 +#define BPF_FUNC_perf_event_read 22 +#define BPF_FUNC_redirect 23 +#define BPF_FUNC_get_route_realm 24 +#define BPF_FUNC_perf_event_output 25 +#define BPF_FUNC_skb_load_bytes 26 +#define BPF_FUNC_get_stackid 27 +#define BPF_FUNC_csum_diff 28 +#define BPF_FUNC_skb_get_tunnel_opt 29 +#define BPF_FUNC_skb_set_tunnel_opt 30 +#define BPF_FUNC_skb_change_proto 31 +#define BPF_FUNC_skb_change_type 32 +#define BPF_FUNC_skb_under_cgroup 33 +#define BPF_FUNC_get_hash_recalc 34 +#define BPF_FUNC_get_current_task 35 +#define BPF_FUNC_probe_write_user 36 +#define BPF_FUNC_current_task_under_cgroup 37 +#define BPF_FUNC_skb_change_tail 38 +#define BPF_FUNC_skb_pull_data 39 +#define BPF_FUNC_csum_update 40 +#define BPF_FUNC_set_hash_invalid 41 +#define BPF_FUNC_get_numa_node_id 42 +#define BPF_FUNC_skb_change_head 43 +#define BPF_FUNC_xdp_adjust_head 44 +#define BPF_FUNC_probe_read_str 45 +#define BPF_FUNC_get_socket_cookie 46 +#define BPF_FUNC_get_socket_uid 47 +#define BPF_FUNC_set_hash 48 +#define BPF_FUNC_setsockopt 49 +#define BPF_FUNC_skb_adjust_room 50 +#define BPF_FUNC_redirect_map 51 +#define BPF_FUNC_sk_redirect_map 52 +#define BPF_FUNC_sock_map_update 53 +#define BPF_FUNC_xdp_adjust_meta 54 +#define BPF_FUNC_perf_event_read_value 55 +#define BPF_FUNC_perf_prog_read_value 56 +#define BPF_FUNC_getsockopt 57 +#define BPF_FUNC_override_return 58 +#define BPF_FUNC_sock_ops_cb_flags_set 59 +#define BPF_FUNC_msg_redirect_map 60 +#define BPF_FUNC_msg_apply_bytes 61 +#define BPF_FUNC_msg_cork_bytes 62 +#define BPF_FUNC_msg_pull_data 63 +#define BPF_FUNC_bind 64 +#define BPF_FUNC_xdp_adjust_tail 65 +#define BPF_FUNC_skb_get_xfrm_state 66 +#define BPF_FUNC_get_stack 67 +#define BPF_FUNC_skb_load_bytes_relative 68 +#define BPF_FUNC_fib_lookup 69 +#define BPF_FUNC_sock_hash_update 70 +#define BPF_FUNC_msg_redirect_hash 71 +#define BPF_FUNC_sk_redirect_hash 72 +#define BPF_FUNC_lwt_push_encap 73 +#define BPF_FUNC_lwt_seg6_store_bytes 74 +#define BPF_FUNC_lwt_seg6_adjust_srh 75 +#define BPF_FUNC_lwt_seg6_action 76 +#define BPF_FUNC_rc_repeat 77 +#define BPF_FUNC_rc_keydown 78 +#define BPF_FUNC_skb_cgroup_id 79 +#define BPF_FUNC_get_current_cgroup_id 80 +#define BPF_FUNC_get_local_storage 81 +#define BPF_FUNC_sk_select_reuseport 82 +#define BPF_FUNC_skb_ancestor_cgroup_id 83 +#define BPF_FUNC_sk_lookup_tcp 84 +#define BPF_FUNC_sk_lookup_udp 85 +#define BPF_FUNC_sk_release 86 +#define BPF_FUNC_map_push_elem 87 +#define BPF_FUNC_map_pop_elem 88 +#define BPF_FUNC_map_peek_elem 89 +#define BPF_FUNC_msg_push_data 90 +#define BPF_FUNC_msg_pop_data 91 +#define BPF_FUNC_rc_pointer_rel 92 +#define BPF_FUNC_spin_lock 93 +#define BPF_FUNC_spin_unlock 94 +#define BPF_FUNC_sk_fullsock 95 +#define BPF_FUNC_tcp_sock 96 +#define BPF_FUNC_skb_ecn_set_ce 97 +#define BPF_FUNC_get_listener_sock 98 +#define BPF_FUNC_skc_lookup_tcp 99 +#define BPF_FUNC_tcp_check_syncookie 100 +#define BPF_FUNC_sysctl_get_name 101 +#define BPF_FUNC_sysctl_get_current_value 102 +#define BPF_FUNC_sysctl_get_new_value 103 +#define BPF_FUNC_sysctl_set_new_value 104 +#define BPF_FUNC_strtol 105 +#define BPF_FUNC_strtoul 106 +#define BPF_FUNC_sk_storage_get 107 +#define BPF_FUNC_sk_storage_delete 108 +#define BPF_FUNC_send_signal 109 +#define BPF_FUNC_tcp_gen_syncookie 110 +#define BPF_FUNC_skb_output 111 +#define BPF_FUNC_probe_read_user 112 +#define BPF_FUNC_probe_read_kernel 113 +#define BPF_FUNC_probe_read_user_str 114 +#define BPF_FUNC_probe_read_kernel_str 115 +#define BPF_FUNC_tcp_send_ack 116 +#define BPF_FUNC_send_signal_thread 117 +#define BPF_FUNC_jiffies64 118 +#define BPF_FUNC_read_branch_records 119 +#define BPF_FUNC_get_ns_current_pid_tgid 120 +#define BPF_FUNC_xdp_output 121 +#define BPF_FUNC_get_netns_cookie 122 +#define BPF_FUNC_get_current_ancestor_cgroup_id 123 +#define BPF_FUNC_sk_assign 124 +#define BPF_FUNC_ktime_get_boot_ns 125 +#define BPF_FUNC_seq_printf 126 +#define BPF_FUNC_seq_write 127 +#define BPF_FUNC_sk_cgroup_id 128 +#define BPF_FUNC_sk_ancestor_cgroup_id 129 +#define BPF_FUNC_ringbuf_output 130 +#define BPF_FUNC_ringbuf_reserve 131 +#define BPF_FUNC_ringbuf_submit 132 +#define BPF_FUNC_ringbuf_discard 133 +#define BPF_FUNC_ringbuf_query 134 +#define BPF_FUNC_csum_level 135 +#define BPF_FUNC_skc_to_tcp6_sock 136 +#define BPF_FUNC_skc_to_tcp_sock 137 +#define BPF_FUNC_skc_to_tcp_timewait_sock 138 +#define BPF_FUNC_skc_to_tcp_request_sock 139 +#define BPF_FUNC_skc_to_udp6_sock 140 +#define BPF_FUNC_get_task_stack 141 +#define BPF_FUNC_load_hdr_opt 142 +#define BPF_FUNC_store_hdr_opt 143 +#define BPF_FUNC_reserve_hdr_opt 144 +#define BPF_FUNC_inode_storage_get 145 +#define BPF_FUNC_inode_storage_delete 146 +#define BPF_FUNC_d_path 147 +#define BPF_FUNC_copy_from_user 148 +#define BPF_FUNC_snprintf_btf 149 +#define BPF_FUNC_seq_printf_btf 150 +#define BPF_FUNC_skb_cgroup_classid 151 +#define BPF_FUNC_redirect_neigh 152 +#define BPF_FUNC_per_cpu_ptr 153 +#define BPF_FUNC_this_cpu_ptr 154 +#define BPF_FUNC_redirect_peer 155 +#define BPF_FUNC_task_storage_get 156 +#define BPF_FUNC_task_storage_delete 157 +#define BPF_FUNC_get_current_task_btf 158 +#define BPF_FUNC_bprm_opts_set 159 +#define BPF_FUNC_ktime_get_coarse_ns 160 +#define BPF_FUNC_ima_inode_hash 161 +#define BPF_FUNC_sock_from_file 162 +#define BPF_FUNC_check_mtu 163 +#define BPF_FUNC_for_each_map_elem 164 +#define BPF_FUNC_snprintf 165 +#define BPF_FUNC_sys_bpf 166 +#define BPF_FUNC_btf_find_by_name_kind 167 +#define BPF_FUNC_sys_close 168 +#define __BPF_FUNC_MAX_ID 169 #define __bpf_md_ptr(type, name) \ union { \ @@ -1157,185 +1327,6 @@ struct btf_ptr { uint32_t flags; }; -/* clang-format off */ -#define __BPF_FUNC_MAPPER(FN) \ - FN(unspec), \ - FN(map_lookup_elem), \ - FN(map_update_elem), \ - FN(map_delete_elem), \ - FN(probe_read), \ - FN(ktime_get_ns), \ - FN(trace_printk), \ - FN(get_prandom_u32), \ - FN(get_smp_processor_id), \ - FN(skb_store_bytes), \ - FN(l3_csum_replace), \ - FN(l4_csum_replace), \ - FN(tail_call), \ - FN(clone_redirect), \ - FN(get_current_pid_tgid), \ - FN(get_current_uid_gid), \ - FN(get_current_comm), \ - FN(get_cgroup_classid), \ - FN(skb_vlan_push), \ - FN(skb_vlan_pop), \ - FN(skb_get_tunnel_key), \ - FN(skb_set_tunnel_key), \ - FN(perf_event_read), \ - FN(redirect), \ - FN(get_route_realm), \ - FN(perf_event_output), \ - FN(skb_load_bytes), \ - FN(get_stackid), \ - FN(csum_diff), \ - FN(skb_get_tunnel_opt), \ - FN(skb_set_tunnel_opt), \ - FN(skb_change_proto), \ - FN(skb_change_type), \ - FN(skb_under_cgroup), \ - FN(get_hash_recalc), \ - FN(get_current_task), \ - FN(probe_write_user), \ - FN(current_task_under_cgroup), \ - FN(skb_change_tail), \ - FN(skb_pull_data), \ - FN(csum_update), \ - FN(set_hash_invalid), \ - FN(get_numa_node_id), \ - FN(skb_change_head), \ - FN(xdp_adjust_head), \ - FN(probe_read_str), \ - FN(get_socket_cookie), \ - FN(get_socket_uid), \ - FN(set_hash), \ - FN(setsockopt), \ - FN(skb_adjust_room), \ - FN(redirect_map), \ - FN(sk_redirect_map), \ - FN(sock_map_update), \ - FN(xdp_adjust_meta), \ - FN(perf_event_read_value), \ - FN(perf_prog_read_value), \ - FN(getsockopt), \ - FN(override_return), \ - FN(sock_ops_cb_flags_set), \ - FN(msg_redirect_map), \ - FN(msg_apply_bytes), \ - FN(msg_cork_bytes), \ - FN(msg_pull_data), \ - FN(bind), \ - FN(xdp_adjust_tail), \ - FN(skb_get_xfrm_state), \ - FN(get_stack), \ - FN(skb_load_bytes_relative), \ - FN(fib_lookup), \ - FN(sock_hash_update), \ - FN(msg_redirect_hash), \ - FN(sk_redirect_hash), \ - FN(lwt_push_encap), \ - FN(lwt_seg6_store_bytes), \ - FN(lwt_seg6_adjust_srh), \ - FN(lwt_seg6_action), \ - FN(rc_repeat), \ - FN(rc_keydown), \ - FN(skb_cgroup_id), \ - FN(get_current_cgroup_id), \ - FN(get_local_storage), \ - FN(sk_select_reuseport), \ - FN(skb_ancestor_cgroup_id), \ - FN(sk_lookup_tcp), \ - FN(sk_lookup_udp), \ - FN(sk_release), \ - FN(map_push_elem), \ - FN(map_pop_elem), \ - FN(map_peek_elem), \ - FN(msg_push_data), \ - FN(msg_pop_data), \ - FN(rc_pointer_rel), \ - FN(spin_lock), \ - FN(spin_unlock), \ - FN(sk_fullsock), \ - FN(tcp_sock), \ - FN(skb_ecn_set_ce), \ - FN(get_listener_sock), \ - FN(skc_lookup_tcp), \ - FN(tcp_check_syncookie), \ - FN(sysctl_get_name), \ - FN(sysctl_get_current_value), \ - FN(sysctl_get_new_value), \ - FN(sysctl_set_new_value), \ - FN(strtol), \ - FN(strtoul), \ - FN(sk_storage_get), \ - FN(sk_storage_delete), \ - FN(send_signal), \ - FN(tcp_gen_syncookie), \ - FN(skb_output), \ - FN(probe_read_user), \ - FN(probe_read_kernel), \ - FN(probe_read_user_str), \ - FN(probe_read_kernel_str), \ - FN(tcp_send_ack), \ - FN(send_signal_thread), \ - FN(jiffies64), \ - FN(read_branch_records), \ - FN(get_ns_current_pid_tgid), \ - FN(xdp_output), \ - FN(get_netns_cookie), \ - FN(get_current_ancestor_cgroup_id), \ - FN(sk_assign), \ - FN(ktime_get_boot_ns), \ - FN(seq_printf), \ - FN(seq_write), \ - FN(sk_cgroup_id), \ - FN(sk_ancestor_cgroup_id), \ - FN(ringbuf_output), \ - FN(ringbuf_reserve), \ - FN(ringbuf_submit), \ - FN(ringbuf_discard), \ - FN(ringbuf_query), \ - FN(csum_level), \ - FN(skc_to_tcp6_sock), \ - FN(skc_to_tcp_sock), \ - FN(skc_to_tcp_timewait_sock), \ - FN(skc_to_tcp_request_sock), \ - FN(skc_to_udp6_sock), \ - FN(get_task_stack), \ - FN(load_hdr_opt), \ - FN(store_hdr_opt), \ - FN(reserve_hdr_opt), \ - FN(inode_storage_get), \ - FN(inode_storage_delete), \ - FN(d_path), \ - FN(copy_from_user), \ - FN(snprintf_btf), \ - FN(seq_printf_btf), \ - FN(skb_cgroup_classid), \ - FN(redirect_neigh), \ - FN(per_cpu_ptr), \ - FN(this_cpu_ptr), \ - FN(redirect_peer), \ - FN(task_storage_get), \ - FN(task_storage_delete), \ - FN(get_current_task_btf), \ - FN(bprm_opts_set), \ - FN(ktime_get_coarse_ns), \ - FN(ima_inode_hash), \ - FN(sock_from_file), \ - FN(check_mtu), \ - FN(for_each_map_elem), \ - FN(snprintf), \ - FN(sys_bpf), \ - FN(btf_find_by_name_kind), \ - FN(sys_close), \ -/* clang-format on */ - -#define __BPF_ENUM_FN(x) BPF_FUNC_##x -enum bpf_func_id { - __BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, -}; -#undef __BPF_ENUM_FN - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_BPF_H_ */ diff --git a/libc/calls/struct/hog.py b/libc/calls/struct/hog.py deleted file mode 100644 index bc69e809d..000000000 --- a/libc/calls/struct/hog.py +++ /dev/null @@ -1,23 +0,0 @@ -s=""" - BPF_SOCK_OPS_VOID, - BPF_SOCK_OPS_TIMEOUT_INIT, - BPF_SOCK_OPS_RWND_INIT, - BPF_SOCK_OPS_TCP_CONNECT_CB, - BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, - BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, - BPF_SOCK_OPS_NEEDS_ECN, - BPF_SOCK_OPS_BASE_RTT, - BPF_SOCK_OPS_RTO_CB, - BPF_SOCK_OPS_RETRANS_CB, - BPF_SOCK_OPS_STATE_CB, - BPF_SOCK_OPS_TCP_LISTEN_CB, - BPF_SOCK_OPS_RTT_CB, - BPF_SOCK_OPS_PARSE_HDR_OPT_CB, - BPF_SOCK_OPS_HDR_OPT_LEN_CB, - BPF_SOCK_OPS_WRITE_HDR_OPT_CB, -""" - -i = 0 -for x in s.replace(',','').split(): - print("#define %s %d" % (x, i)) - i += 1 diff --git a/libc/calls/struct/ucontext-netbsd.internal.h b/libc/calls/struct/ucontext-netbsd.internal.h new file mode 100644 index 000000000..a0c60e247 --- /dev/null +++ b/libc/calls/struct/ucontext-netbsd.internal.h @@ -0,0 +1,84 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_NETBSD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_NETBSD_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +// clang-format off +#include "libc/calls/ucontext.h" + +#define __UCONTEXT_SIZE 784 +#define _UC_SIGMASK 0x01 +#define _UC_STACK 0x02 +#define _UC_CPU 0x04 +#define _UC_FPU 0x08 +#define _UC_TLSBASE 0x00080000 +#define _UC_SETSTACK 0x00010000 +#define _UC_CLRSTACK 0x00020000 +#define _UC_CLRSTACK 0x00020000 + +union sigval_netbsd { + int32_t sival_int; + void *sival_ptr; +}; + +struct sigset_netbsd { + uint32_t __bits[4]; +}; + +struct stack_netbsd { + void *ss_sp; + size_t ss_size; + int32_t ss_flags; +}; + +struct mcontext_netbsd { + union { + struct { + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rbp; + uint64_t rbx; + uint64_t rax; + uint64_t gs; + uint64_t fs; + uint64_t es; + uint64_t ds; + uint64_t trapno; + uint64_t err; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; + }; + int64_t __gregs[26]; + }; + int64_t _mc_tlsbase; + struct FpuState __fpregs; +}; + +struct ucontext_netbsd { + union { + struct { + uint32_t uc_flags; /* see _UC_* above */ + struct ucontext_netbsd *uc_link; + struct sigset_netbsd uc_sigmask; + struct stack_netbsd uc_stack; + struct mcontext_netbsd uc_mcontext; + }; + char __pad[__UCONTEXT_SIZE]; + }; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_NETBSD_INTERNAL_H_ */ diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index f55c7e226..1cbcc3907 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -19,7 +19,11 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/errno.h" +#include "libc/intrin/once.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/symboliclink.h" #include "libc/nt/enum/tokeninformationclass.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" @@ -27,27 +31,12 @@ #include "libc/nt/runtime.h" #include "libc/nt/struct/luid.h" #include "libc/nt/struct/tokenprivileges.h" +#include "libc/nt/thunk/msabi.h" #include "libc/sysv/errfuns.h" -static bool g_can_symlink; +__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; -textwindows int sys_symlinkat_nt(const char *target, int newdirfd, - const char *linkpath) { - uint32_t flags; - char16_t target16[PATH_MAX]; - char16_t linkpath16[PATH_MAX]; - if (!g_can_symlink) return eacces(); - flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0; - if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; - if (__mkntpath(target, target16) == -1) return -1; - if (CreateSymbolicLink(linkpath16, target16, flags)) { - return 0; - } else { - return __winerr(); - } -} - -static textstartup bool EnableSymlink(void) { +static bool AllowedToCreateSymlinks(void) { int64_t tok; struct NtLuid id; struct NtTokenPrivileges tp; @@ -60,10 +49,45 @@ static textstartup bool EnableSymlink(void) { return GetLastError() != kNtErrorNotAllAssigned; } -static textstartup void g_can_symlink_init() { - g_can_symlink = EnableSymlink(); -} +textwindows int sys_symlinkat_nt(const char *target, int newdirfd, + const char *linkpath) { + int targetlen; + uint32_t attrs, flags; + char16_t target16[PATH_MAX]; + char16_t linkpath16[PATH_MAX]; -const void *const g_can_symlink_ctor[] initarray = { - g_can_symlink_init, -}; + // convert the paths + if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; + if ((targetlen = __mkntpath(target, target16)) == -1) return -1; + + // determine if we need directory flag + if ((attrs = __imp_GetFileAttributesW(target16)) != -1u) { + if (attrs & kNtFileAttributeDirectory) { + flags = kNtSymbolicLinkFlagDirectory; + } else { + flags = 0; + } + } else { + // win32 needs to know if it's a directory of a file symlink, but + // that's hard to determine if we're creating a broken symlink so + // we'll fall back to checking for trailing slash + if (targetlen && target16[targetlen - 1] == '\\') { + flags = kNtSymbolicLinkFlagDirectory; + } else { + flags = 0; + } + } + + // windows only lets administrators do this + // even then we're required to ask for permission + if (!_once(AllowedToCreateSymlinks())) { + return eperm(); + } + + // we can now proceed + if (CreateSymbolicLink(linkpath16, target16, flags)) { + return 0; + } else { + return __fix_enotdir(-1, linkpath16); + } +} diff --git a/libc/calls/symlinkat.c b/libc/calls/symlinkat.c index 8f23366e3..26242b377 100644 --- a/libc/calls/symlinkat.c +++ b/libc/calls/symlinkat.c @@ -34,7 +34,7 @@ * @param target can be relative and needn't exist * @param linkpath is what gets created * @return 0 on success, or -1 w/ errno - * @note Windows NT only lets admins do this + * @raise EPERM if a non-admin on Windows NT tries to use this * @asyncsignalsafe */ int symlinkat(const char *target, int newdirfd, const char *linkpath) { diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index 01d82ec37..29c0819b5 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -25,15 +25,17 @@ #include "libc/nt/runtime.h" textwindows int sys_truncate_nt(const char *path, uint64_t length) { + int rc; bool32 ok; int64_t fh; uint16_t path16[PATH_MAX]; if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtGenericWrite, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) { - ok = sys_ftruncate_nt(fh, length); + rc = sys_ftruncate_nt(fh, length); CloseHandle(fh); - if (ok) return 0; + } else { + rc = -1; } - return __winerr(); + return __fix_enotdir(rc, path16); } diff --git a/libc/calls/ucontext.h b/libc/calls/ucontext.h index df4e59af2..24450230b 100644 --- a/libc/calls/ucontext.h +++ b/libc/calls/ucontext.h @@ -5,56 +5,6 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define REG_R8 REG_R8 -#define REG_R9 REG_R9 -#define REG_R10 REG_R10 -#define REG_R11 REG_R11 -#define REG_R12 REG_R12 -#define REG_R13 REG_R13 -#define REG_R14 REG_R14 -#define REG_R15 REG_R15 -#define REG_RDI REG_RDI -#define REG_RSI REG_RSI -#define REG_RBP REG_RBP -#define REG_RBX REG_RBX -#define REG_RDX REG_RDX -#define REG_RAX REG_RAX -#define REG_RCX REG_RCX -#define REG_RSP REG_RSP -#define REG_RIP REG_RIP -#define REG_EFL REG_EFL -#define REG_CSGSFS REG_CSGSFS -#define REG_ERR REG_ERR -#define REG_TRAPNO REG_TRAPNO -#define REG_OLDMASK REG_OLDMASK -#define REG_CR2 REG_CR2 - -enum GeneralRegister { - REG_R8, - REG_R9, - REG_R10, - REG_R11, - REG_R12, - REG_R13, - REG_R14, - REG_R15, - REG_RDI, - REG_RSI, - REG_RBP, - REG_RBX, - REG_RDX, - REG_RAX, - REG_RCX, - REG_RSP, - REG_RIP, - REG_EFL, - REG_CSGSFS, - REG_ERR, - REG_TRAPNO, - REG_OLDMASK, - REG_CR2 -}; - struct XmmRegister { uint64_t u64[2]; }; diff --git a/libc/calls/unlinkat-nt.c b/libc/calls/unlinkat-nt.c index ccaf15072..3b8555f03 100644 --- a/libc/calls/unlinkat-nt.c +++ b/libc/calls/unlinkat-nt.c @@ -140,5 +140,5 @@ textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) { // rc = SyncDirectory(dirfd, path16, n); } } - return rc; + return __fix_enotdir(rc, path16); } diff --git a/libc/calls/utimensat-sysv.c b/libc/calls/utimensat-sysv.c index 407f08ad0..5c4e87550 100644 --- a/libc/calls/utimensat-sysv.c +++ b/libc/calls/utimensat-sysv.c @@ -23,8 +23,6 @@ #include "libc/time/time.h" #include "libc/zipos/zipos.internal.h" -#define __NR_utimensat_linux 0x118 /*RHEL5:CVE-2010-3301*/ - int sys_utimensat(int dirfd, const char *path, const struct timespec ts[2], int flags) { int rc, olderr; @@ -35,8 +33,7 @@ int sys_utimensat(int dirfd, const char *path, const struct timespec ts[2], if (!IsXnu()) { olderr = errno; rc = __sys_utimensat(dirfd, path, ts, flags); - if (((rc == -1 && errno == ENOSYS) || rc == __NR_utimensat_linux) && - dirfd == AT_FDCWD && !flags) { + if ((rc == -1 && errno == ENOSYS) && dirfd == AT_FDCWD && !flags) { errno = olderr; if (ts) { tv[0].tv_sec = ts[0].tv_sec; diff --git a/libc/calls/virtualmax.c b/libc/calls/virtualmax.c index 7c8f8359e..92df148c7 100644 --- a/libc/calls/virtualmax.c +++ b/libc/calls/virtualmax.c @@ -25,4 +25,4 @@ * * By default no limit is imposed. */ -size_t __virtualmax; +size_t __virtualmax = -1; diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 737be07b9..4999f6ce8 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -16,22 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/strace.internal.h" -#include "libc/calls/struct/iovec.h" -#include "libc/calls/struct/siginfo.h" -#include "libc/calls/typedef/sigaction_f.h" -#include "libc/intrin/kprintf.h" -#include "libc/limits.h" #include "libc/nt/errors.h" -#include "libc/nt/files.h" #include "libc/nt/runtime.h" -#include "libc/nt/struct/overlapped.h" -#include "libc/runtime/internal.h" -#include "libc/sock/internal.h" -#include "libc/str/str.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" @@ -44,16 +32,20 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, _offset2overlap(g_fds.p[fd].handle, offset, &overlap))) { return sent; } - err = GetLastError(); - // make sure write() raises SIGPIPE on broken pipe - // make sure write() raises SIGPIPE on closing named pipe - if (err == kNtErrorBrokenPipe || err == kNtErrorNoData) { - __sig_raise(SIGPIPE, SI_KERNEL); - return epipe(); + switch (GetLastError()) { + // case kNtErrorInvalidHandle: + // return ebadf(); /* handled by consts.sh */ + // case kNtErrorNotEnoughQuota: + // return edquot(); /* handled by consts.sh */ + case kNtErrorBrokenPipe: // broken pipe + case kNtErrorNoData: // closing named pipe + __sig_raise(SIGPIPE, SI_KERNEL); // + return epipe(); // + case kNtErrorAccessDenied: // write doesn't return EACCESS + return ebadf(); // + default: + return __winerr(); } - // kNtErrorInvalidHandle → EBADF (consts.sh) - // kNtErrorNotEnoughQuota → EDQUOT (consts.sh; SetProcessWorkingSetSize) - return __winerr(); } textwindows ssize_t sys_write_nt(int fd, const struct iovec *iov, size_t iovlen, diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index d5b104bb2..78dd24dd2 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -44,6 +44,7 @@ #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" @@ -58,7 +59,7 @@ STATIC_YOINK("_init_asan"); #define ASAN_MORGUE_ITEMS 512 #define ASAN_MORGUE_THRESHOLD 65536 // morgue memory O(ITEMS*THRESHOLD) -#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin +#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin /** * @fileoverview Cosmopolitan Address Sanitizer Runtime. @@ -156,9 +157,10 @@ struct ReportOriginHeap { bool __asan_noreentry; static struct AsanMorgue __asan_morgue; -static wontreturn void __asan_unreachable(void) { - for (;;) __builtin_trap(); -} +#define __asan_unreachable() \ + do { \ + for (;;) __builtin_trap(); \ + } while (0) static int __asan_bsf(uint64_t x) { _Static_assert(sizeof(long long) == sizeof(uint64_t), ""); @@ -177,7 +179,8 @@ static uint64_t __asan_roundup2pow(uint64_t x) { static char *__asan_utf8cpy(char *p, unsigned c) { uint64_t z; z = tpenc(c); - do *p++ = z; + do + *p++ = z; while ((z >>= 8)); return p; } @@ -921,7 +924,8 @@ static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { if (!__asan_checka(SHADOW(bp), sizeof(*bp) >> 3).kind) { addr = bp->addr; if (addr == weakaddr("__gc") && weakaddr("__gc")) { - do --gi; + do + --gi; while ((addr = garbage->p[gi].ret) == weakaddr("__gc")); } bt->p[i] = addr; @@ -1172,10 +1176,7 @@ void __asan_stack_free(char *p, size_t size, int classid) { } void __asan_handle_no_return(void) { - uintptr_t stk, ssz; - stk = (uintptr_t)__builtin_frame_address(0); - ssz = GetStackSize(); - __asan_unpoison(stk, ROUNDUP(stk, ssz) - stk); + __asan_unpoison(GetStackAddr(0), GetStackSize()); } void __asan_register_globals(struct AsanGlobal g[], int n) { @@ -1239,14 +1240,14 @@ void __asan_unpoison_stack_memory(uintptr_t addr, size_t size) { __asan_unpoison(addr, size); } -void __asan_alloca_poison(uintptr_t addr, size_t size) { +void __asan_alloca_poison(uintptr_t addr, uintptr_t size) { __asan_poison(addr - 32, 32, kAsanAllocaUnderrun); __asan_poison(addr + size, 32, kAsanAllocaOverrun); - __asan_unpoison(addr, size); } void __asan_allocas_unpoison(uintptr_t x, uintptr_t y) { - if (x && x > y) __asan_unpoison(x, y - x); + if (!x || x > y) return; + __asan_memset((void *)((x >> 3) + 0x7fff8000), 0, (y - x) / 8); } void *__asan_addr_is_in_fake_stack(void *fakestack, void *addr, void **beg, diff --git a/libc/intrin/createsymboliclink.greg.c b/libc/intrin/createsymboliclink.greg.c new file mode 100644 index 000000000..ddbe879a0 --- /dev/null +++ b/libc/intrin/createsymboliclink.greg.c @@ -0,0 +1,39 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/nt/files.h" + +__msabi extern typeof(CreateSymbolicLink) *const __imp_CreateSymbolicLinkW; + +/** + * Creates symbolic link on the New Technology. + * @note you need to elevate process privileges before calling this + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +bool32 CreateSymbolicLink(const char16_t *lpSymlinkFileName, + const char16_t *lpTargetPathName, uint32_t dwFlags) { + bool32 ok; + ok = __imp_CreateSymbolicLinkW(lpSymlinkFileName, lpTargetPathName, dwFlags); + if (!ok) __winerr(); + NTTRACE("CreateSymbolicLink(%#hs, %#hs, %s) → %hhhd% m", lpSymlinkFileName, + lpTargetPathName, DescribeNtSymbolicLinkFlags(dwFlags), ok); + return ok; +} diff --git a/libc/intrin/deletefile.greg.c b/libc/intrin/deletefile.greg.c index 0038afd6a..bf30eb534 100644 --- a/libc/intrin/deletefile.greg.c +++ b/libc/intrin/deletefile.greg.c @@ -19,7 +19,6 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/nt/files.h" -#include "libc/nt/memory.h" #include "libc/nt/thunk/msabi.h" __msabi extern typeof(DeleteFile) *const __imp_DeleteFileW; diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index ed6259b70..521319d8a 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -1,8 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBEFLAGS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_INTRIN_DESCRIBEFLAGS_INTERNAL_H_ +#include "libc/nt/struct/securityattributes.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#include "libc/nt/struct/securityattributes.h" struct thatispacked DescribeFlags { unsigned flag; @@ -26,7 +26,9 @@ const char *DescribeNtPipeOpenFlags(uint32_t); const char *DescribeNtPipeModeFlags(uint32_t); const char *DescribeNtFileShareFlags(uint32_t); const char *DescribeNtFileAccessFlags(uint32_t); +const char *DescribeNtSymbolicLinkFlags(uint32_t); const char *DescribeNtProcessAccessFlags(uint32_t); +const char *DescribeNtMoveFileInputFlags(uint32_t); const char *DescribeNtCreationDisposition(uint32_t); const char *DescribeNtConsoleModeInputFlags(uint32_t); const char *DescribeNtConsoleModeOutputFlags(uint32_t); diff --git a/libc/intrin/describentmovefileflags.greg.c b/libc/intrin/describentmovefileflags.greg.c new file mode 100644 index 000000000..9c5c62026 --- /dev/null +++ b/libc/intrin/describentmovefileflags.greg.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/movefileexflags.h" + +static const struct DescribeFlags kMoveFileInputFlags[] = { + {kNtMovefileReplaceExisting, "ReplaceExisting"}, // + {kNtMovefileCopyAllowed, "CopyAllowed"}, // + {kNtMovefileDelayUntilReboot, "DelayUntilReboot"}, // + {kNtMovefileWriteThrough, "WriteThrough"}, // + {kNtMovefileCreateHardlink, "CreateHardlink"}, // + {kNtMovefileFailIfNotTrackable, "FailIfNotTrackable"}, // +}; + +const char *DescribeNtMoveFileInputFlags(uint32_t x) { + static char movefileflags[256]; + return DescribeFlags(movefileflags, sizeof(movefileflags), + kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags), + "kNtMovefile", x); +} diff --git a/libc/intrin/describentsymboliclinkflags.greg.c b/libc/intrin/describentsymboliclinkflags.greg.c new file mode 100644 index 000000000..3f4afffc3 --- /dev/null +++ b/libc/intrin/describentsymboliclinkflags.greg.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/symboliclink.h" + +static const struct DescribeFlags kSymbolicLinkflags[] = { + {kNtSymbolicLinkFlagDirectory, "Directory"}, // + {kNtSymbolicLinkFlagAllowUnprivilegedCreate, "AllowUnprivilegedCreate"}, // +}; + +const char *DescribeNtSymbolicLinkFlags(uint32_t x) { + static char ntsymboliclinkflags[64]; + return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags), + kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags), + "kNtSymbolicLinkFlag", x); +} diff --git a/libc/intrin/exit1.greg.c b/libc/intrin/exit1.greg.c index cbe1ed625..2b20fec0e 100644 --- a/libc/intrin/exit1.greg.c +++ b/libc/intrin/exit1.greg.c @@ -16,8 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" +#include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/intrin/winthread.internal.h" +#include "libc/mem/mem.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" #include "libc/sysv/consts/nr.h" @@ -31,6 +35,7 @@ * @noreturn */ privileged wontreturn void _Exit1(int rc) { + struct WinThread *wt; STRACE("_Exit1(%d)", rc); if (!IsWindows() && !IsMetal()) { asm volatile("syscall" @@ -39,6 +44,10 @@ privileged wontreturn void _Exit1(int rc) { : "rcx", "r11", "memory"); __builtin_unreachable(); } else if (IsWindows()) { + if ((wt = GetWinThread())) { + __releasefd(wt->pid); + weaken(free)(wt); + } ExitThread(rc); } for (;;) { diff --git a/libc/calls/g_fds.c b/libc/intrin/g_fds.c similarity index 100% rename from libc/calls/g_fds.c rename to libc/intrin/g_fds.c diff --git a/libc/calls/g_fds_init.S b/libc/intrin/g_fds_init.S similarity index 100% rename from libc/calls/g_fds_init.S rename to libc/intrin/g_fds_init.S diff --git a/libc/intrin/getfileattributes.greg.c b/libc/intrin/getfileattributes.greg.c index b7dcefb7b..c7dcc4c00 100644 --- a/libc/intrin/getfileattributes.greg.c +++ b/libc/intrin/getfileattributes.greg.c @@ -28,7 +28,7 @@ __msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; /** * Gets file info on the New Technology. * - * @return handle, or -1 on failure + * @return handle, or -1u on failure * @note this wrapper takes care of ABI, STRACE(), and __winerr() */ textwindows uint32_t GetFileAttributes(const char16_t *lpPathName) { diff --git a/libc/intrin/getpid.c b/libc/intrin/getpid.c index d13708d33..0d8113aed 100644 --- a/libc/intrin/getpid.c +++ b/libc/intrin/getpid.c @@ -18,8 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" - -extern int __pid; +#include "libc/runtime/internal.h" /** * Returns process id. diff --git a/libc/intrin/gettid.c b/libc/intrin/gettid.greg.c similarity index 93% rename from libc/intrin/gettid.c rename to libc/intrin/gettid.greg.c index 3e6b16f1b..4a8f92703 100644 --- a/libc/intrin/gettid.c +++ b/libc/intrin/gettid.greg.c @@ -18,6 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/intrin/tls.h" +#include "libc/intrin/winthread.internal.h" #include "libc/nt/thread.h" /** @@ -27,6 +29,7 @@ int gettid(void) { int rc; int64_t wut; + struct WinThread *wt; if (IsLinux()) { asm("syscall" @@ -71,7 +74,11 @@ int gettid(void) { } if (IsWindows()) { - return GetCurrentThreadId(); + if ((wt = GetWinThread())) { + return wt->pid; + } else { + return GetCurrentThreadId(); + } } return getpid(); diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 9a6235214..b8d9ddd76 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -64,8 +64,10 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \ -ffreestanding \ $(NO_MAGIC) +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/createfile.greg.o \ o/$(MODE)/libc/intrin/reopenfile.greg.o \ o/$(MODE)/libc/intrin/deletefile.greg.o \ diff --git a/libc/intrin/kdos2errno.S b/libc/intrin/kdos2errno.S index f15e02f7f..5640a4a12 100644 --- a/libc/intrin/kdos2errno.S +++ b/libc/intrin/kdos2errno.S @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/nt/errors.h" +#include "libc/nt/errors.h" #include "libc/macros.internal.h" // @fileoverview data structure for __dos2errno() @@ -101,8 +102,8 @@ kDos2Errno: .e kNtErrorBadLength,EACCES .e kNtErrorBadNetpath,ENOENT .e kNtErrorBadNetName,ENOENT - .e kNtErrorBadNetResp,ENETDOWN .e kNtErrorBadPathname,ENOENT + .e kNtErrorBadNetResp,ENETDOWN .e kNtErrorFileExists,EEXIST .e kNtErrorCannotMake,EACCES .e kNtErrorCommitmentLimit,ENOMEM @@ -118,6 +119,7 @@ kDos2Errno: .e kNtErrorHostDown,EHOSTUNREACH .e kNtErrorHostUnreachable,EHOSTUNREACH .e kNtErrorInsufficientBuffer,EFAULT + .e kNtErrorNoaccess,EFAULT .e kNtErrorInvalidAddress,EADDRNOTAVAIL .e kNtErrorNotAReparsePoint,EINVAL .e kNtErrorInvalidFunction,EINVAL @@ -130,7 +132,6 @@ kDos2Errno: .e kNtErrorNetworkAccessDenied,EACCES .e kNtErrorNetworkBusy,ENETDOWN .e kNtErrorNetworkUnreachable,ENETUNREACH - .e kNtErrorNoaccess,EFAULT .e kNtErrorNonpagedSystemResources,ENOMEM .e kNtErrorNotEnoughMemory,ENOMEM .e kNtErrorNotEnoughQuota,ENOMEM diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 89a57c9bd..39d604a92 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -39,6 +39,7 @@ #include "libc/nt/runtime.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" +#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" @@ -54,7 +55,7 @@ struct Timestamps { unsigned long long start; }; -extern int __pid; +extern bool __threaded; unsigned long long __kbirth; // see fork-nt.c privileged static struct Timestamps kenter(void) { @@ -361,7 +362,12 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, case 'P': if (!__vforked) { - x = __pid; + if (!__threaded) { + x = __pid; + } else { + // clone() is linked and it yoinks gettid() + x = weaken(gettid)(); + } } else { asm volatile("syscall" : "=a"(x) @@ -444,7 +450,8 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, i = 0; m = (1 << base) - 1; if (hash && x) sign = hash; - do z[i++ & 127] = abet[x & m]; + do + z[i++ & 127] = abet[x & m]; while ((x >>= base) || (pdot && i < prec)); goto EmitNumber; diff --git a/libc/intrin/movefileex.greg.c b/libc/intrin/movefileex.greg.c new file mode 100644 index 000000000..8f497ab8c --- /dev/null +++ b/libc/intrin/movefileex.greg.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/memory.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(MoveFileEx) *const __imp_MoveFileExW; + +/** + * Deletes existing empty directory. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +textwindows bool32 MoveFileEx(const char16_t *lpExistingFileName, + const char16_t *lpNewFileName, int dwFlags) { + bool32 ok; + ok = __imp_MoveFileExW(lpExistingFileName, lpNewFileName, dwFlags); + if (!ok) __winerr(); + NTTRACE("MoveFileEx(%#hs, %#hs, %s) → %hhhd% m", lpExistingFileName, + lpNewFileName, DescribeNtMoveFileInputFlags(dwFlags), ok); + return ok; +} diff --git a/libc/intrin/once.h b/libc/intrin/once.h new file mode 100644 index 000000000..52eb74f2d --- /dev/null +++ b/libc/intrin/once.h @@ -0,0 +1,21 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ +#define COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ +#include "libc/intrin/spinlock.h" + +#define _once(x) \ + ({ \ + typeof(x) oncerc; \ + static bool once; \ + static typeof(oncerc) onceresult; \ + _Alignas(64) static char oncelock; \ + _spinlock(&oncelock); \ + if (once) { \ + oncerc = onceresult; \ + } else { \ + oncerc = onceresult = x; \ + } \ + _spunlock(&oncelock); \ + oncerc; \ + }) + +#endif /* COSMOPOLITAN_LIBC_INTRIN_ONCE_H_ */ diff --git a/libc/intrin/refcount.h b/libc/intrin/refcount.h new file mode 100644 index 000000000..076de6a03 --- /dev/null +++ b/libc/intrin/refcount.h @@ -0,0 +1,7 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_REFCOUNT_H_ +#define COSMOPOLITAN_LIBC_INTRIN_REFCOUNT_H_ + +#define _incref(x) __atomic_fetch_add(x, 1, __ATOMIC_RELAXED) +#define _decref(x) __atomic_sub_fetch(x, 1, __ATOMIC_SEQ_CST) + +#endif /* COSMOPOLITAN_LIBC_INTRIN_REFCOUNT_H_ */ diff --git a/libc/calls/releasefd.c b/libc/intrin/releasefd.c similarity index 97% rename from libc/calls/releasefd.c rename to libc/intrin/releasefd.c index 58cd4bff6..3b9410d36 100644 --- a/libc/calls/releasefd.c +++ b/libc/intrin/releasefd.c @@ -21,10 +21,10 @@ #include "libc/macros.internal.h" void __releasefd(int fd) { + _spinlock(&__fds_lock); if (0 <= fd && fd < g_fds.n) { - _spinlock(&__fds_lock); g_fds.p[fd].kind = 0; g_fds.f = MIN(fd, g_fds.f); - _spunlock(&__fds_lock); } + _spunlock(&__fds_lock); } diff --git a/libc/intrin/removedirectory.greg.c b/libc/intrin/removedirectory.greg.c index 3fcf0aac5..6f6035c54 100644 --- a/libc/intrin/removedirectory.greg.c +++ b/libc/intrin/removedirectory.greg.c @@ -19,13 +19,12 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/nt/files.h" -#include "libc/nt/memory.h" #include "libc/nt/thunk/msabi.h" __msabi extern typeof(RemoveDirectory) *const __imp_RemoveDirectoryW; /** - * Deletes existing empty directory. + * Deletes existing empty directory on the New Technology. * @note this wrapper takes care of ABI, STRACE(), and __winerr() */ textwindows bool32 RemoveDirectory(const char16_t *lpPathName) { diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h index a5ba7d630..e8cdffaa2 100644 --- a/libc/intrin/spinlock.h +++ b/libc/intrin/spinlock.h @@ -1,9 +1,14 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ #define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 401 && \ - !defined(__STRICT_ANSI__) +#ifdef TINY +#define _spinlock(lock) \ + do { \ + while (__sync_lock_test_and_set(lock, 1)) { \ + __builtin_ia32_pause(); \ + } \ + } while (0) +#else #define _spinlock(lock) \ do { \ for (;;) { \ @@ -16,9 +21,8 @@ } \ } \ } while (0) +#endif #define _spunlock(lock) __sync_lock_release(lock) -#endif /* GNU 4.1+ */ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */ diff --git a/libc/intrin/threaded.c b/libc/intrin/threaded.c new file mode 100644 index 000000000..0971a44a6 --- /dev/null +++ b/libc/intrin/threaded.c @@ -0,0 +1,20 @@ +/*-*- 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ + +bool __threaded; diff --git a/libc/intrin/tls.greg.c b/libc/intrin/tls.greg.c new file mode 100644 index 000000000..2dec7159a --- /dev/null +++ b/libc/intrin/tls.greg.c @@ -0,0 +1,89 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/dce.h" +#include "libc/intrin/tls.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(TlsFree) *const __imp_TlsFree; +__msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc; +__msabi extern typeof(TlsGetValue) *const __imp_TlsGetValue; +__msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue; + +/** + * Assigns thread-local storage slot. + * + * This function may for instance be called at startup and the result + * can be assigned to a global static variable; from then on, all the + * threads in your application may pass that value to TlsGetValue, to + * retrieve their thread-local values. + * + * @return index on success, or -1u w/ errno + * @threadsafe + */ +uint32_t TlsAlloc(void) { + return __imp_TlsAlloc(); +} + +/** + * Releases thread-local storage slot. + * @threadsafe + */ +bool32 TlsFree(uint32_t dwTlsIndex) { + return __imp_TlsFree(dwTlsIndex); +} + +/** + * Sets value to thread-local storage slot. + * + * @param dwTlsIndex is something returned by TlsAlloc() + * @return true if successful, otherwise false + * @threadsafe + */ +bool32 TlsSetValue(uint32_t dwTlsIndex, void *lpTlsValue) { + assert(IsWindows()); + if (dwTlsIndex < 64) { + asm("mov\t%1,%%gs:%0" + : "=m"(*((long *)0x1480 + dwTlsIndex)) + : "r"(lpTlsValue)); + return true; + } else { + return __imp_TlsSetValue(dwTlsIndex, lpTlsValue); + } +} + +/** + * Retrieves value from thread-local storage slot. + * + * @param dwTlsIndex is something returned by TlsAlloc() + * @return true if successful, otherwise false + * @threadsafe + */ +void *TlsGetValue(uint32_t dwTlsIndex) { + void *lpTlsValue; + assert(IsWindows()); + if (dwTlsIndex < 64) { + asm("mov\t%%gs:%1,%0" + : "=r"(lpTlsValue) + : "m"(*((long *)0x1480 + dwTlsIndex))); + return lpTlsValue; + } else { + return __imp_TlsGetValue(dwTlsIndex); + } +} diff --git a/libc/intrin/tls.h b/libc/intrin/tls.h new file mode 100644 index 000000000..8f539900d --- /dev/null +++ b/libc/intrin/tls.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_TLS_H_ +#define COSMOPOLITAN_LIBC_INTRIN_TLS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +uint32_t TlsAlloc(void); +bool32 TlsFree(uint32_t); +bool32 TlsSetValue(uint32_t, void *); +void *TlsGetValue(uint32_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_TLS_H_ */ diff --git a/libc/intrin/ubsan.c b/libc/intrin/ubsan.c index dcad76e49..20c015296 100644 --- a/libc/intrin/ubsan.c +++ b/libc/intrin/ubsan.c @@ -301,7 +301,7 @@ static __ubsan_die_f *__ubsan_type_mismatch_handler( p = __stpcpy(p, " align "); p = __intcpy(p, info->alignment); } else { - p = __stpcpy(p, "insufficient size%n\t"); + p = __stpcpy(p, "insufficient size "); p = __stpcpy(p, kind); p = __stpcpy(p, " address 0x"); p = __fixcpy(p, pointer, sizeof(pointer) * CHAR_BIT); diff --git a/libc/str/zipfindcentraldir.c b/libc/intrin/winthread.c similarity index 65% rename from libc/str/zipfindcentraldir.c rename to libc/intrin/winthread.c index 42a966d66..89c193fbe 100644 --- a/libc/str/zipfindcentraldir.c +++ b/libc/intrin/winthread.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,32 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/zip.h" - -/* TODO(jart): DELETE */ +#include "libc/dce.h" +#include "libc/intrin/tls.h" +#include "libc/intrin/winthread.internal.h" /** - * Locates End Of Central Directory record in ZIP file. - * - * We search backwards for the End Of Central Directory Record magnum. - * The ZIP spec says this can be anywhere in the last 64kb. We go all - * the way since .com.dbg binaries will have lots of DWARF stuff after - * the embedded .com binary. As such, we sanity check the other fields - * too to make sure the record seems legit and it's not just a random - * occurrence of the magnum. - * - * @param p points to file memory - * @param n is byte size of file + * @fileoverview TLS slot for clone() win32 polyfill. */ -uint8_t *zipfindcentraldir(const uint8_t *p, size_t n) { - size_t i; - if (n >= kZipCdirHdrMinSize) { - i = n - kZipCdirHdrMinSize; - do { - if (ZIP_CDIR_MAGIC(p + i) == kZipCdirHdrMagic && IsZipCdir32(p, n, i)) { - return (/*unconst*/ uint8_t *)(p + i); - } - } while (i--); + +int __winthread; + +static textstartup void __winthread_init(void) { + if (IsWindows()) { + __winthread = TlsAlloc(); + TlsSetValue(__winthread, 0); } - return NULL; } + +const void *const __winthread_ctor[] initarray = { + __winthread_init, +}; diff --git a/libc/intrin/winthread.internal.h b/libc/intrin/winthread.internal.h new file mode 100644 index 000000000..2464dbd58 --- /dev/null +++ b/libc/intrin/winthread.internal.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ +#include "libc/intrin/tls.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct WinThread { + int pid; +}; + +extern int __winthread; + +static inline struct WinThread *GetWinThread(void) { + return TlsGetValue(__winthread); +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_ */ diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 05771904f..1416bc3ec 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -64,8 +64,6 @@ relegated static void ShowFunctionCalls(ucontext_t *ctx) { struct StackFrame goodframe; if (!ctx->uc_mcontext.rip) { kprintf("%s is NULL can't show backtrace%n", "RIP"); - } else if (!ctx->uc_mcontext.rbp) { - kprintf("%s is NULL can't show backtrace%n", "RBP"); } else { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; @@ -142,7 +140,7 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { } } DescribeCpuFlags( - p, ctx->uc_mcontext.gregs[REG_EFL], + p, ctx->uc_mcontext.eflags, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->swd : 0, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->mxcsr : 0); kprintf("%s%n", buf); diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index ff4459909..64a83772d 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -45,11 +45,12 @@ static struct timespec vflogf_ts; */ void vflogf_onfail(FILE *f) { errno_t err; - struct stat st; + int64_t size; if (IsTiny()) return; err = ferror(f); if (fileno(f) != -1 && (err == ENOSPC || err == EDQUOT || err == EFBIG) && - (fstat(fileno(f), &st) == -1 || st.st_size > kNontrivialSize)) { + ((size = getfiledescriptorsize(fileno(f))) == -1 || + size > kNontrivialSize)) { ftruncate(fileno(f), 0); fseek(f, SEEK_SET, 0); f->beg = f->end = 0; diff --git a/libc/nt/enum/movefileexflags.h b/libc/nt/enum/movefileexflags.h index 190207162..cf2976f0d 100644 --- a/libc/nt/enum/movefileexflags.h +++ b/libc/nt/enum/movefileexflags.h @@ -4,8 +4,8 @@ #define kNtMovefileReplaceExisting 1 #define kNtMovefileCopyAllowed 2 #define kNtMovefileDelayUntilReboot 4 +#define kNtMovefileWriteThrough 8 #define kNtMovefileCreateHardlink 16 #define kNtMovefileFailIfNotTrackable 32 -#define kNtMovefileWriteThrough 8 #endif /* COSMOPOLITAN_LIBC_NT_ENUM_MOVEFILEEXFLAGS_H_ */ diff --git a/libc/nt/enum/symboliclink.h b/libc/nt/enum/symboliclink.h new file mode 100644 index 000000000..d19da7e04 --- /dev/null +++ b/libc/nt/enum/symboliclink.h @@ -0,0 +1,7 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_SYMBOLICLINK_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_SYMBOLICLINK_H_ + +#define kNtSymbolicLinkFlagDirectory 1 +#define kNtSymbolicLinkFlagAllowUnprivilegedCreate 2 + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_SYMBOLICLINK_H_ */ diff --git a/libc/nt/files.h b/libc/nt/files.h index 0d84caace..97c95a83f 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -44,8 +44,6 @@ #define kNtDuplicateCloseSource 1 #define kNtDuplicateSameAccess 2 -#define kNtSymbolicLinkFlagDirectory 1 - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/nt/kernel32/CreateSymbolicLinkW.s b/libc/nt/kernel32/CreateSymbolicLinkW.s index 7dd4dcb60..4fa19cf9a 100644 --- a/libc/nt/kernel32/CreateSymbolicLinkW.s +++ b/libc/nt/kernel32/CreateSymbolicLinkW.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_CreateSymbolicLinkW,CreateSymbolicLinkW,0 .text.windows -CreateSymbolicLink: +__CreateSymbolicLink: push %rbp mov %rsp,%rbp .profilable mov __imp_CreateSymbolicLinkW(%rip),%rax jmp __sysv2nt - .endfn CreateSymbolicLink,globl + .endfn __CreateSymbolicLink,globl .previous diff --git a/libc/nt/kernel32/MoveFileExW.s b/libc/nt/kernel32/MoveFileExW.s index c05159558..4ca9cb47a 100644 --- a/libc/nt/kernel32/MoveFileExW.s +++ b/libc/nt/kernel32/MoveFileExW.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_MoveFileExW,MoveFileExW,0 .text.windows -MoveFileEx: +__MoveFileEx: push %rbp mov %rsp,%rbp .profilable mov __imp_MoveFileExW(%rip),%rax jmp __sysv2nt - .endfn MoveFileEx,globl + .endfn __MoveFileEx,globl .previous diff --git a/libc/nt/kernel32/TlsAlloc.s b/libc/nt/kernel32/TlsAlloc.s index 40d390207..e39628b00 100644 --- a/libc/nt/kernel32/TlsAlloc.s +++ b/libc/nt/kernel32/TlsAlloc.s @@ -1,2 +1,14 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_TlsAlloc,TlsAlloc,0 + + .text.windows +__TlsAlloc: + push %rbp + mov %rsp,%rbp + .profilable + sub $32,%rsp + call *__imp_TlsAlloc(%rip) + leave + ret + .endfn __TlsAlloc,globl + .previous diff --git a/libc/nt/kernel32/TlsFree.s b/libc/nt/kernel32/TlsFree.s index 1c09fd7d0..8a66707ad 100644 --- a/libc/nt/kernel32/TlsFree.s +++ b/libc/nt/kernel32/TlsFree.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_TlsFree,TlsFree,0 + + .text.windows +__TlsFree: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_TlsFree(%rip) + leave + ret + .endfn __TlsFree,globl + .previous diff --git a/libc/nt/kernel32/TlsGetValue.s b/libc/nt/kernel32/TlsGetValue.s index 491efefae..371ed9084 100644 --- a/libc/nt/kernel32/TlsGetValue.s +++ b/libc/nt/kernel32/TlsGetValue.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_TlsGetValue,TlsGetValue,0 + + .text.windows +__TlsGetValue: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_TlsGetValue(%rip) + leave + ret + .endfn __TlsGetValue,globl + .previous diff --git a/libc/nt/kernel32/TlsSetValue.s b/libc/nt/kernel32/TlsSetValue.s index 7cdb3d286..77d63bf4b 100644 --- a/libc/nt/kernel32/TlsSetValue.s +++ b/libc/nt/kernel32/TlsSetValue.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_TlsSetValue,TlsSetValue,0 + + .text.windows +__TlsSetValue: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_TlsSetValue(%rip),%rax + jmp __sysv2nt + .endfn __TlsSetValue,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 0eaac8ade..582ae5adf 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -196,7 +196,6 @@ imp 'CreateSemaphore' CreateSemaphoreW kernel32 0 imp 'CreateSemaphoreA' CreateSemaphoreA kernel32 232 imp 'CreateSemaphoreEx' CreateSemaphoreExW kernel32 0 imp 'CreateSemaphoreExA' CreateSemaphoreExA kernel32 233 -imp 'CreateSymbolicLink' CreateSymbolicLinkW kernel32 0 3 imp 'CreateSymbolicLinkA' CreateSymbolicLinkA kernel32 0 3 imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238 imp 'CreateSymbolicLinkTransactedA' CreateSymbolicLinkTransactedA kernel32 237 @@ -884,7 +883,6 @@ imp 'Module32First' Module32FirstW kernel32 992 imp 'Module32Next' Module32NextW kernel32 994 imp 'MoveFile' MoveFileW kernel32 1000 2 imp 'MoveFileA' MoveFileA kernel32 995 2 -imp 'MoveFileEx' MoveFileExW kernel32 0 3 imp 'MoveFileExA' MoveFileExA kernel32 996 3 imp 'MoveFileTransacted' MoveFileTransactedW kernel32 999 imp 'MoveFileTransactedA' MoveFileTransactedA kernel32 998 @@ -1228,10 +1226,6 @@ imp 'TermsrvSetValueKey' TermsrvSetValueKey kernel32 1441 imp 'TermsrvSyncUserIniFileExt' TermsrvSyncUserIniFileExt kernel32 1442 imp 'Thread32First' Thread32First kernel32 1443 imp 'Thread32Next' Thread32Next kernel32 1444 -imp 'TlsAlloc' TlsAlloc kernel32 0 -imp 'TlsFree' TlsFree kernel32 0 -imp 'TlsGetValue' TlsGetValue kernel32 0 -imp 'TlsSetValue' TlsSetValue kernel32 0 imp 'Toolhelp32ReadProcessMemory' Toolhelp32ReadProcessMemory kernel32 1449 imp 'TransactNamedPipe' TransactNamedPipe kernel32 0 7 imp 'TransmitCommChar' TransmitCommChar kernel32 0 @@ -1353,6 +1347,7 @@ imp '__CreateFileMappingNuma' CreateFileMappingNumaW kernel32 0 7 imp '__CreateNamedPipe' CreateNamedPipeW kernel32 0 8 imp '__CreatePipe' CreatePipe kernel32 0 4 imp '__CreateProcess' CreateProcessW kernel32 0 10 +imp '__CreateSymbolicLink' CreateSymbolicLinkW kernel32 0 3 imp '__CreateThread' CreateThread kernel32 0 6 imp '__DeleteFile' DeleteFileW kernel32 0 1 imp '__DeviceIoControl' DeviceIoControl kernel32 0 8 @@ -1365,11 +1360,16 @@ imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 imp '__GetFileAttributes' GetFileAttributesW kernel32 0 1 imp '__MapViewOfFileEx' MapViewOfFileEx kernel32 0 6 imp '__MapViewOfFileExNuma' MapViewOfFileExNuma kernel32 0 7 +imp '__MoveFileEx' MoveFileExW kernel32 0 3 imp '__OpenProcess' OpenProcess kernel32 0 3 imp '__ReOpenFile' ReOpenFile kernel32 0 4 # TODO(jart): 6.2 and higher imp '__RemoveDirectory' RemoveDirectoryW kernel32 0 1 imp '__SetCurrentDirectory' SetCurrentDirectoryW kernel32 0 1 imp '__TerminateProcess' TerminateProcess kernel32 0 2 +imp '__TlsAlloc' TlsAlloc kernel32 0 0 +imp '__TlsFree' TlsFree kernel32 0 1 +imp '__TlsGetValue' TlsGetValue kernel32 0 1 +imp '__TlsSetValue' TlsSetValue kernel32 0 2 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 diff --git a/libc/nt/struct/teb.h b/libc/nt/struct/teb.h index cf1e7f008..23d4d9c16 100644 --- a/libc/nt/struct/teb.h +++ b/libc/nt/struct/teb.h @@ -21,7 +21,7 @@ #define _NtGetFib() gs((void **)(0x20)) #define _NtGetEnv() gs((char16_t **)(0x38)) #define _NtGetRpc() gs((void **)(0x50)) -#define _NtGetTls() gs((void **)(0x58)) +#define _NtGetTls() gs((void **)(0x58)) /* cf. gs((long *)0x1480 + i0..64) */ #endif /* __GNUC__ && !__STRICT_ANSI__ */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index 254b46c4f..6d24f19ee 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -24,11 +24,11 @@ #include "libc/nexgen32e/rdtsc.h" #include "libc/nt/thread.h" #include "libc/rand/rand.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/auxv.h" #include "libc/thread/create.h" -extern int __pid; static int thepid; static uint128_t thepool; _Alignas(64) static char rand64_lock; @@ -36,10 +36,10 @@ _Alignas(64) static char rand64_lock; /** * Returns nondeterministic random data. * - * This function is similar to lemur64() except it'thepool intended to - * be unpredictable. This PRNG automatically seeds itself on startup - * using a much stronger and faster random source than `srand(time(0))`. - * This function will automatically reseed itself when new processes and + * This function is similar to lemur64() except that it's intended to be + * unpredictable. This PRNG automatically seeds itself on startup using + * a much stronger and faster random source than `srand(time(0))`. This + * function will automatically reseed itself when new processes and * threads are spawned. This function is thread safe in the sense that a * race condition can't happen where two threads return the same result. * diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c deleted file mode 100644 index 821a0257f..000000000 --- a/libc/runtime/clone.c +++ /dev/null @@ -1,117 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 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/internal.h" -#include "libc/calls/sig.internal.h" -#include "libc/dce.h" -#include "libc/nexgen32e/nt2sysv.h" -#include "libc/nexgen32e/stackframe.h" -#include "libc/nt/runtime.h" -#include "libc/nt/thread.h" -#include "libc/sysv/consts/clone.h" -#include "libc/sysv/consts/nr.h" -#include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/sicode.h" -#include "libc/sysv/errfuns.h" - -struct WinThread { - int (*func)(void *); - void *param; - void *stack; -}; - -static noasan textwindows uint32_t WinThreadMain(void *param) { - struct WinThread *wt = param; - asm volatile("mov\t%%rbp,%%r14\n\t" - "mov\t%%rsp,%%r15\n\t" - "xor\t%%ebp,%%ebp\n\t" - "mov\t%2,%%rsp\n\t" - "call\t*%0\n\t" - "mov\t%%r14,%%rbp\n\t" - "mov\t%%r15,%%rsp" - : /* no outputs */ - : "m"(wt->func), "D"(wt->param), "m"(wt->stack) - : "r14", "r15", "memory"); - return 0; -} - -/** - * Creates thread. - * - * @param flags usually has one of - * - `CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` for threads - * - `CLONE_VFORK|CLONE_VM|SIGCHLD` for vfork() - * - `SIGCHLD` for fork() - * as part high bytes, and the low order byte may optionally contain - * a signal e.g. SIGCHLD, to enable parent notification on terminate - */ -privileged int clone(int (*f)(void *), void *stack, int flags, void *arg, ...) { - int tidfd; - va_list va; - intptr_t ax; - uint32_t tid; - int64_t hand; - int32_t *ptid; - register void *tls asm("r8"); - register int32_t *ctid asm("r10"); - register int (*func)(void *) asm("r9"); - if (IsLinux() || IsNetbsd()) { - va_start(va, arg); - ptid = va_arg(va, int32_t *); - tls = va_arg(va, void *); - ctid = va_arg(va, int32_t *); - va_end(va); - func = f; - stack = (void *)(((uintptr_t)stack & -16) - 8); - *(intptr_t *)stack = (intptr_t)arg; - asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_clone), "D"(flags), "S"(stack), "d"(ptid), - "r"(ctid), "r"(tls), "r"(func) - : "rcx", "r11", "memory"); - if (ax) return ax; - asm volatile("xor\t%%ebp,%%ebp\n\t" - "pop\t%%rdi\n\t" - "call\t%0\n\t" - "xchg\t%%eax,%%edi\n\t" - "call\t_Exit1" - : /* no outputs */ - : "r"(func) - : "memory"); - unreachable; - } else if (IsWindows()) { - if ((tidfd = __reservefd(-1)) == -1) return -1; - if (flags == CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND) { - if ((hand = CreateThread(&kNtIsInheritable, 0, NT2SYSV(WinThreadMain), - &(struct WinThread){f, arg, stack}, 0, &tid))) { - // XXX: this should be tracked in a separate data structure - g_fds.p[tidfd].kind = kFdProcess; - g_fds.p[tidfd].handle = hand; - g_fds.p[tidfd].flags = O_CLOEXEC; - g_fds.p[tidfd].zombie = false; - return tidfd; - } else { - return -1; - } - } else { - return einval(); - } - } else { - return enosys(); - } -} diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 79b15176c..0211d37ac 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -42,6 +42,7 @@ #include "libc/nt/signals.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/runtime/directmap.internal.h" +#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/ntstdin.internal.h" @@ -51,7 +52,6 @@ STATIC_YOINK("_check_sigchld"); -extern int __pid; extern int64_t __wincrashearly; extern unsigned long long __kbirth; extern unsigned char __data_start[]; /* αpε */ @@ -60,7 +60,7 @@ extern unsigned char __bss_start[]; /* αpε */ extern unsigned char __bss_end[]; /* αpε */ bool32 __onntconsoleevent_nt(uint32_t); -static textwindows wontreturn void KillForkChild(const char *func) { +static textwindows wontreturn void AbortFork(const char *func) { STRACE("fork() %s() failed %d", func, GetLastError()); ExitProcess(177); } @@ -100,7 +100,25 @@ static dontinline textwindows bool WriteAll(int64_t h, void *buf, size_t n) { static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) { if (!ForkIo2(h, buf, n, ReadFile, "ReadFile")) { - KillForkChild("ReadFile"); + AbortFork("ReadFile"); + } +} + +static textwindows int64_t MapOrDie(uint32_t prot, uint64_t size) { + int64_t h; + if ((h = CreateFileMapping(-1, 0, prot, size >> 32, size, 0))) { + return h; + } else { + AbortFork("MapOrDie"); + } +} + +static textwindows void ViewOrDie(int64_t h, uint32_t access, size_t pos, + size_t size, void *base) { + void *got; + got = MapViewOfFileEx(h, access, pos >> 32, pos, size, base); + if (!got || (base && got != base)) { + AbortFork("ViewOrDie"); } } @@ -135,16 +153,13 @@ textwindows void WinMainForked(void) { ReadOrDie(reader, &mapcount, sizeof(_mmi.i)); ReadOrDie(reader, &mapcapacity, sizeof(_mmi.n)); specialz = ROUNDUP(mapcapacity * sizeof(_mmi.p[0]), kMemtrackGran); - MapViewOfFileEx( - CreateFileMapping(-1, 0, kNtPageReadwrite, specialz >> 32, specialz, 0), - kNtFileMapWrite, 0, 0, specialz, maps); + ViewOrDie(MapOrDie(kNtPageReadwrite, specialz), kNtFileMapWrite, 0, specialz, + maps); ReadOrDie(reader, maps, mapcount * sizeof(_mmi.p[0])); if (IsAsan()) { shad = (char *)(((intptr_t)maps >> 3) + 0x7fff8000); size = ROUNDUP(specialz >> 3, FRAMESIZE); - MapViewOfFileEx( - CreateFileMapping(-1, 0, kNtPageReadwrite, size >> 32, size, 0), - kNtFileMapWrite, 0, 0, size, maps); + ViewOrDie(MapOrDie(kNtPageReadwrite, size), kNtFileMapWrite, 0, size, maps); ReadOrDie(reader, shad, (mapcount * sizeof(_mmi.p[0])) >> 3); } @@ -156,18 +171,15 @@ textwindows void WinMainForked(void) { 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 - maps[i].h = CreateFileMapping(-1, 0, kNtPageExecuteReadwrite, - upsize >> 32, upsize, 0); - MapViewOfFileEx(maps[i].h, kNtFileMapWrite | kNtFileMapExecute, 0, 0, - upsize, addr); + ViewOrDie((maps[i].h = MapOrDie(kNtPageExecuteReadwrite, upsize)), + kNtFileMapWrite | kNtFileMapExecute, 0, upsize, addr); ReadOrDie(reader, addr, size); } else { // we can however safely inherit MAP_SHARED with zero copy - MapViewOfFileEx(maps[i].h, - maps[i].readonlyfile - ? kNtFileMapRead | kNtFileMapExecute - : kNtFileMapWrite | kNtFileMapExecute, - maps[i].offset >> 32, maps[i].offset, size, addr); + ViewOrDie(maps[i].h, + maps[i].readonlyfile ? kNtFileMapRead | kNtFileMapExecute + : kNtFileMapWrite | kNtFileMapExecute, + maps[i].offset, size, addr); } } @@ -187,13 +199,13 @@ textwindows void WinMainForked(void) { for (i = 0; i < mapcount; ++i) { if (!VirtualProtect((void *)((uint64_t)maps[i].x << 16), maps[i].size, __prot2nt(maps[i].prot, maps[i].iscow), &oldprot)) { - KillForkChild("VirtualProtect"); + AbortFork("VirtualProtect"); } } // mitosis complete if (!CloseHandle(reader)) { - KillForkChild("CloseHandle"); + AbortFork("CloseHandle"); } // rewrap the stdin named pipe hack @@ -223,9 +235,9 @@ textwindows int sys_fork_nt(void) { char **args, **args2; char16_t pipename[64]; int64_t reader, writer; + struct NtStartupInfo startinfo; int i, n, pid, untrackpid, rc = -1; char *p, forkvar[6 + 21 + 1 + 21 + 1]; - struct NtStartupInfo startinfo; struct NtProcessInformation procinfo; if (!setjmp(jb)) { pid = untrackpid = __reservefd(-1); diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 14a94670b..ae9d242f3 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -23,8 +23,7 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/nt/process.h" - -extern int __pid; +#include "libc/runtime/internal.h" /** * Creates new process. diff --git a/libc/runtime/getsymboltable.c b/libc/runtime/getsymboltable.greg.c similarity index 100% rename from libc/runtime/getsymboltable.c rename to libc/runtime/getsymboltable.greg.c diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index 98e20e67e..a6fea7f8d 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigset.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/log/libfatal.internal.h" #include "libc/runtime/runtime.h" @@ -58,8 +59,10 @@ privileged noinstrument noasan int __hook(void *ifunc, intptr_t kProgramCodeStart = (intptr_t)&_ereal; intptr_t kPrivilegedStart = (intptr_t)&__privileged_start; bool kIsBinaryAligned = !(kPrivilegedStart & (PAGESIZE - 1)); - sigfillset(&mask); - sigprocmask(SIG_BLOCK, &mask, &oldmask); + if (!IsWindows()) { + sigfillset(&mask); + sys_sigprocmask(SIG_BLOCK, &mask, &oldmask); + } if ((rc = mprotect( (void *)symbols->addr_base, kPrivilegedStart - symbols->addr_base, kIsBinaryAligned ? PROT_READ | PROT_WRITE @@ -127,6 +130,8 @@ privileged noinstrument noasan int __hook(void *ifunc, mprotect((void *)symbols->addr_base, kPrivilegedStart - symbols->addr_base, PROT_READ | PROT_EXEC); } - sigprocmask(SIG_SETMASK, &oldmask, NULL); + if (!IsWindows()) { + sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); + } return rc; } diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index ea3209ac7..468d0d401 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -12,6 +12,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +extern int __pid; extern uint32_t __ntconsolemode[3]; extern const char v_ntsubsystem[] __attribute__((__weak__)); extern const uintptr_t __fini_array_end[] __attribute__((__weak__)); diff --git a/libc/runtime/memtrack.greg.c b/libc/runtime/memtrack.greg.c index b32d14125..b59e8b64e 100644 --- a/libc/runtime/memtrack.greg.c +++ b/libc/runtime/memtrack.greg.c @@ -76,16 +76,10 @@ static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { if (IsAsan()) { shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000); dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0); - if (!dm.addr) { - STRACE("ExtendMemoryIntervals() fail #1"); - return false; - } + if (!dm.addr) return false; } dm = sys_mmap(base, gran, prot, flags, -1, 0); - if (!dm.addr) { - STRACE("ExtendMemoryIntervals() fail #2"); - return false; - } + if (!dm.addr) return false; MoveMemoryIntervals(dm.addr, mm->p, mm->i); mm->p = dm.addr; mm->n = gran / sizeof(*mm->p); @@ -95,16 +89,10 @@ static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { if (IsAsan()) { shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000); dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0); - if (!dm.addr) { - STRACE("ExtendMemoryIntervals() fail #3"); - return false; - } + if (!dm.addr) return false; } dm = sys_mmap(base, gran, prot, flags, -1, 0); - if (!dm.addr) { - STRACE("ExtendMemoryIntervals() fail #4"); - return false; - } + if (!dm.addr) return false; mm->n = (size + gran) / sizeof(*mm->p); } assert(AreMemoryIntervalsOk(mm)); diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 7f81645e4..4e5895277 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -27,6 +27,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/limits.h" #include "libc/log/backtrace.internal.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" @@ -281,7 +282,7 @@ noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd, } else if (__isfdkind(fd, kFdZip)) { STRACE("fd is zipos handle"); res = VIP(einval()); - } else if (__virtualmax && + } else if (__virtualmax < LONG_MAX && (__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), size, &virtualneed) || virtualneed > __virtualmax)) { diff --git a/libc/runtime/opensymboltable.c b/libc/runtime/opensymboltable.greg.c similarity index 92% rename from libc/runtime/opensymboltable.c rename to libc/runtime/opensymboltable.greg.c index df60863dd..e126a84ec 100644 --- a/libc/runtime/opensymboltable.c +++ b/libc/runtime/opensymboltable.greg.c @@ -28,6 +28,7 @@ #include "libc/elf/struct/shdr.h" #include "libc/elf/struct/sym.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" @@ -51,7 +52,7 @@ ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ (size_t)(e)->e_shentsize * (i))) -noasan static char *GetStrtab(Elf64_Ehdr *e, size_t *n) { +static char *GetStrtab(Elf64_Ehdr *e, size_t *n) { char *name; Elf64_Half i; Elf64_Shdr *shdr; @@ -68,7 +69,7 @@ noasan static char *GetStrtab(Elf64_Ehdr *e, size_t *n) { return 0; } -noasan static Elf64_Sym *GetSymtab(Elf64_Ehdr *e, Elf64_Xword *n) { +static Elf64_Sym *GetSymtab(Elf64_Ehdr *e, Elf64_Xword *n) { Elf64_Half i; Elf64_Shdr *shdr; for (i = e->e_shnum; i > 0; --i) { @@ -82,7 +83,7 @@ noasan static Elf64_Sym *GetSymtab(Elf64_Ehdr *e, Elf64_Xword *n) { return 0; } -noasan static void GetImageRange(Elf64_Ehdr *elf, intptr_t *x, intptr_t *y) { +static void GetImageRange(Elf64_Ehdr *elf, intptr_t *x, intptr_t *y) { unsigned i; Elf64_Phdr *phdr; intptr_t start, end, pstart, pend; @@ -105,11 +106,11 @@ noasan static void GetImageRange(Elf64_Ehdr *elf, intptr_t *x, intptr_t *y) { * * @return object freeable with CloseSymbolTable(), or NULL w/ errno */ -noasan struct SymbolTable *OpenSymbolTable(const char *filename) { +struct SymbolTable *OpenSymbolTable(const char *filename) { int fd; void *map; long *stp; - struct stat st; + ssize_t filesize; unsigned i, j, x; const Elf64_Ehdr *elf; const char *name_base; @@ -119,10 +120,10 @@ noasan struct SymbolTable *OpenSymbolTable(const char *filename) { ptrdiff_t names_offset, name_base_offset, stp_offset; map = MAP_FAILED; if ((fd = open(filename, O_RDONLY)) == -1) return 0; - if (fstat(fd, &st) == -1) goto SystemError; - if (st.st_size > INT_MAX) goto RaiseE2big; - if (st.st_size < 64) goto RaiseEnoexec; - elf = map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ((filesize = getfiledescriptorsize(fd)) == -1) goto SystemError; + if (filesize > INT_MAX) goto RaiseE2big; + if (filesize < 64) goto RaiseEnoexec; + elf = map = mmap(0, filesize, PROT_READ, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) goto SystemError; if (READ32LE(map) != READ32LE("\177ELF")) goto RaiseEnoexec; if (!(name_base = GetStrtab(map, &m))) goto RaiseEnobufs; @@ -181,7 +182,7 @@ noasan struct SymbolTable *OpenSymbolTable(const char *filename) { } t->count = j; munmap(stp, ROUNDUP(sizeof(const Elf64_Sym *) * n, FRAMESIZE)); - munmap(map, st.st_size); + munmap(map, filesize); close(fd); return t; RaiseE2big: @@ -195,7 +196,7 @@ RaiseEnoexec: SystemError: STRACE("OpenSymbolTable()% m"); if (map != MAP_FAILED) { - munmap(map, st.st_size); + munmap(map, filesize); } close(fd); return 0; diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 8e4728080..c6a4273ce 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -240,9 +240,9 @@ textstartup void __printargs(const char *prologue) { PRINT("FILE DESCRIPTORS"); for (i = 0; i < ARRAYLEN(pfds); ++i) { pfds[i].fd = i; - pfds[i].events = POLLIN | POLLOUT; + pfds[i].events = POLLIN; } - if ((n = poll(pfds, ARRAYLEN(pfds), 20)) != -1) { + if ((n = poll(pfds, ARRAYLEN(pfds), 0)) != -1) { for (i = 0; i < ARRAYLEN(pfds); ++i) { if (i && (pfds[i].revents & POLLNVAL)) continue; PRINT(" ☼ %d (revents=%#hx F_GETFL=%#x)", i, pfds[i].revents, diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 5a929505b..f8e42ef33 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -58,11 +58,8 @@ $(LIBC_RUNTIME_A).pkg: \ $(foreach x,$(LIBC_RUNTIME_A_DIRECTDEPS),$($(x)_A).pkg) o/$(MODE)/libc/runtime/fork-nt.o \ -o/$(MODE)/libc/runtime/printf.o \ -o/$(MODE)/libc/runtime/abort-nt.o \ o/$(MODE)/libc/runtime/printmemoryintervals.o \ o/$(MODE)/libc/runtime/arememoryintervalsok.o \ -o/$(MODE)/libc/runtime/assertfail.o \ o/$(MODE)/libc/runtime/directmap.o \ o/$(MODE)/libc/runtime/directmapnt.o \ o/$(MODE)/libc/runtime/findmemoryinterval.o \ @@ -75,12 +72,14 @@ o/$(MODE)/libc/runtime/hook.greg.o \ o/$(MODE)/libc/runtime/isheap.o \ o/$(MODE)/libc/runtime/memtrack.o \ o/$(MODE)/libc/runtime/memtracknt.o \ +o/$(MODE)/libc/runtime/metalprintf.greg.o \ o/$(MODE)/libc/runtime/printargs.greg.o \ o/$(MODE)/libc/runtime/mman.greg.o \ o/$(MODE)/libc/runtime/print.greg.o \ o/$(MODE)/libc/runtime/stackchkfail.o \ o/$(MODE)/libc/runtime/stackchkfaillocal.o \ o/$(MODE)/libc/runtime/winmain.greg.o \ +o/$(MODE)/libc/runtime/opensymboltable.o \ o/$(MODE)/libc/runtime/getsymboltable.greg.o: \ OVERRIDE_CFLAGS += \ -ffreestanding \ @@ -96,6 +95,14 @@ o/$(MODE)/libc/runtime/qsort.o: \ OVERRIDE_CFLAGS += \ -Og +# make always linked runtimes less huge when it's profitable +o//libc/runtime/mmap.o \ +o//libc/runtime/munmap.o \ +o//libc/runtime/memtrack.greg.o \ +o//libc/runtime/opensymboltable.greg.o: \ + OVERRIDE_CFLAGS += \ + -Os + o/$(MODE)/libc/runtime/ftrace.greg.o: \ OVERRIDE_CFLAGS += \ -mgeneral-regs-only diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index ba3c83b0c..789094553 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -62,7 +62,7 @@ extern char ape_stack_align[] __attribute__((__weak__)); * Returns address of bottom of stack. */ #define GetStackAddr(ADDEND) \ - (((intptr_t)__builtin_frame_address(0) & -GetStackSize()) + (ADDEND)) + ((((intptr_t)__builtin_frame_address(0) - 1) & -GetStackSize()) + (ADDEND)) /** * Returns preferred bottom address of stack. diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index eb0347694..c18f52418 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -25,6 +25,7 @@ #include "libc/elf/pf2prot.internal.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/libfatal.internal.h" @@ -86,7 +87,6 @@ struct WinArgs { char envblock[ARG_MAX]; }; -extern int __pid; extern uint32_t __winmainpid; extern int64_t __wincrashearly; extern const char kConsoleHandles[3]; @@ -164,10 +164,15 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { for (i = 0; i < 3; ++i) { hand = GetStdHandle(kConsoleHandles[i]); rc = GetConsoleMode(hand, __ntconsolemode + i); - NTTRACE("GetConsoleMode(%p, [%#x]) → %hhhd", hand, __ntconsolemode[i], + NTTRACE("GetConsoleMode(%p, [%s]) → %hhhd", hand, + i ? DescribeNtConsoleModeOutputFlags(__ntconsolemode[i]) + : DescribeNtConsoleModeInputFlags(__ntconsolemode[i]), rc); rc = SetConsoleMode(hand, kConsoleModes[i]); - NTTRACE("SetConsoleMode(%p, %#x) → %hhhd", hand, kConsoleModes[i], rc); + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hand, + i ? DescribeNtConsoleModeOutputFlags(kConsoleModes[i]) + : DescribeNtConsoleModeInputFlags(kConsoleModes[i]), + rc); } } _mmi.p = _mmi.s; diff --git a/libc/sock/ntstdin.greg.c b/libc/sock/ntstdin.greg.c index 9531f59e3..b435a91da 100644 --- a/libc/sock/ntstdin.greg.c +++ b/libc/sock/ntstdin.greg.c @@ -21,6 +21,8 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/refcount.h" +#include "libc/intrin/spinlock.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/nt2sysv.h" #include "libc/nt/createfile.h" @@ -47,7 +49,7 @@ static textwindows uint32_t StdinWorkerThread(void *arg) { struct NtStdinWorker w, *wp = arg; NTTRACE("StdinWorkerThread(%ld → %ld → %ld) pid %d tid %d", wp->reader, wp->writer, wp->consumer, getpid(), gettid()); - __sync_lock_release(&wp->sync); + _spunlock(&wp->sync); w = *wp; do { ok = ReadFile(w.reader, buf, sizeof(buf), &got, 0); @@ -95,9 +97,7 @@ textwindows struct NtStdinWorker *NewNtStdinWorker(int fd) { kNtFileFlagOverlapped, 0)) != -1) { if ((w->worker = CreateThread(0, 0, NT2SYSV(StdinWorkerThread), w, 0, &w->tid)) != -1) { - while (__sync_lock_test_and_set(&w->sync, __ATOMIC_CONSUME)) { - __builtin_ia32_pause(); - } + _spinlock(&w->sync); g_fds.p[fd].handle = w->consumer; g_fds.p[fd].worker = w; return w; @@ -116,7 +116,7 @@ textwindows struct NtStdinWorker *NewNtStdinWorker(int fd) { * @return worker object for new fd */ textwindows struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *w) { - __atomic_fetch_add(&w->refs, 1, __ATOMIC_RELAXED); + _incref(&w->refs); return w; } @@ -127,7 +127,7 @@ textwindows struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *w) { */ textwindows bool UnrefNtStdinWorker(struct NtStdinWorker *w) { bool ok = true; - if (__atomic_sub_fetch(&w->refs, 1, __ATOMIC_SEQ_CST)) return true; + if (_decref(&w->refs)) return true; if (!CloseHandle(w->consumer)) ok = false; if (!CloseHandle(w->writer)) ok = false; if (!CloseHandle(w->reader)) ok = false; diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index 21f0c0622..be831b5c8 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -24,6 +24,8 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/dirent.h" #include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nt/enum/fileflagandattributes.h" @@ -129,6 +131,7 @@ static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) { if ((res->fd = FindFirstFile(name, &res->windata)) != -1) { return res; } + __fix_enotdir(-1, name); free(res); } } else { @@ -141,13 +144,17 @@ static textwindows dontinline DIR *opendir_nt(const char *path) { int len; DIR *res; char16_t *name; - if ((name = malloc(PATH_MAX * 2))) { - if ((len = __mkntpath(path, name)) != -1 && - (res = opendir_nt_impl(name, len))) { - res->name = name; - return res; + if (*path) { + if ((name = malloc(PATH_MAX * 2))) { + if ((len = __mkntpath(path, name)) != -1 && + (res = opendir_nt_impl(name, len))) { + res->name = name; + return res; + } + free(name); } - free(name); + } else { + enoent(); } return NULL; } @@ -236,7 +243,11 @@ DIR *opendir(const char *name) { struct stat st; struct Zipos *zip; struct ZiposUri zipname; - if (weaken(__zipos_get) && weaken(__zipos_parseuri)(name, &zipname) != -1) { + if (!name || (IsAsan() && !__asan_is_valid(name, 1))) { + efault(); + res = 0; + } else if (weaken(__zipos_get) && + weaken(__zipos_parseuri)(name, &zipname) != -1) { if (weaken(__zipos_stat)(&zipname, &st) != -1) { if (S_ISDIR(st.st_mode)) { zip = weaken(__zipos_get)(); diff --git a/libc/str/kstrsignal.S b/libc/str/kstrsignal.S index 65b1e5253..838354752 100644 --- a/libc/str/kstrsignal.S +++ b/libc/str/kstrsignal.S @@ -64,6 +64,7 @@ kStrSignal: .e SIGRTMAX,"RTMAX" .e SIGRTMIN,"RTMIN" .e SIGEMT,"EMT" + .e SIGPWR,"PWR" .long 0 .endobj kStrSignal,globl,hidden .overrun diff --git a/libc/sysv/calls/__sys_sigprocmask.s b/libc/sysv/calls/__sys_sigprocmask.s new file mode 100644 index 000000000..a9cc61ef9 --- /dev/null +++ b/libc/sysv/calls/__sys_sigprocmask.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_sigprocmask,0x125030154214900e,globl,hidden diff --git a/libc/sysv/calls/clone.s b/libc/sysv/calls/clone.s deleted file mode 100644 index 928425d4c..000000000 --- a/libc/sysv/calls/clone.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall clone,0xfffffffffffff038,globl diff --git a/libc/sysv/calls/sys_clone.s b/libc/sysv/calls/sys_clone.s new file mode 100644 index 000000000..476bf70ef --- /dev/null +++ b/libc/sysv/calls/sys_clone.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_clone,0x11fffffffffff038,globl,hidden diff --git a/libc/sysv/calls/sys_sigprocmask.s b/libc/sysv/calls/sys_sigprocmask.s deleted file mode 100644 index 862061b05..000000000 --- a/libc/sysv/calls/sys_sigprocmask.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_sigprocmask,0x125030154214900e,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index d8910826b..ab7554733 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -171,9 +171,9 @@ syscon sig SIGINFO 0 29 29 29 29 0 # bsd consensus syscon sig SIGRTMAX 64 0 126 0 63 0 syscon sig SIGRTMIN 32 0 65 0 33 0 syscon sig SIGEMT 0 7 7 7 7 0 # not implemented in most community editions of system five; consider doing this using SIGUSR1 or SIGUSR2 instead +syscon sig SIGPWR 30 30 30 30 32 30 # not implemented in most community editions of system five; consider doing this using SIGUSR1 or SIGUSR2 instead syscon compat SIGPOLL 29 23 23 23 23 29 # same as SIGIO syscon compat SIGIOT 6 6 6 6 6 6 # PDP-11 feature; same as SIGABRT -syscon compat SIGPWR 30 30 30 30 32 30 # not implemented in most community editions of system five; consider doing this using SIGUSR1 or SIGUSR2 instead # open() flags # @@ -512,7 +512,7 @@ syscon rlimit RLIMIT_RSS 5 5 5 5 5 127 # max physical memory size syscon rlimit RLIMIT_NPROC 6 7 7 7 7 127 # max number of processes; see fork()→EAGAIN; bsd consensus syscon rlimit RLIMIT_NOFILE 7 8 8 8 8 127 # max number of open files; see accept()→EMFILE/ENFILE; bsd consensus syscon rlimit RLIMIT_MEMLOCK 8 6 6 6 6 127 # max locked-in-memory address space; bsd consensus -syscon rlimit RLIMIT_AS 9 5 10 2 10 9 # max virtual memory size in bytes; this one actually works; fudged as RLIMIT_DATA on OpenBSD +syscon rlimit RLIMIT_AS 9 5 10 2 10 0 # max virtual memory size in bytes; this one actually works; fudged as RLIMIT_DATA on OpenBSD syscon rlimit RLIMIT_LOCKS 10 127 127 127 127 127 # max flock() / fcntl() locks; bsd consensus syscon rlimit RLIMIT_SIGPENDING 11 127 127 127 127 127 # max sigqueue() can enqueue; bsd consensus syscon rlimit RLIMIT_MSGQUEUE 12 127 127 127 127 127 # meh posix message queues; bsd consensus @@ -527,10 +527,10 @@ syscon compat RLIMIT_VMEM 9 5 10 127 10 127 # same as RLIMIT_AS # resource limit special values # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon rlim RLIM_NLIMITS 16 9 15 9 12 0 # no clue why we need it -syscon rlim RLIM_INFINITY 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0 -syscon rlim RLIM_SAVED_CUR 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0 -syscon rlim RLIM_SAVED_MAX 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0 +syscon rlim RLIM_NLIMITS 16 9 15 9 12 1 +syscon rlim RLIM_INFINITY 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0xffffffffffffffff +syscon rlim RLIM_SAVED_CUR 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0xffffffffffffffff +syscon rlim RLIM_SAVED_MAX 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff 0xffffffffffffffff # sigaction() codes # @@ -2577,7 +2577,6 @@ syscon nr __NR_thr_create 0xfff 0xfff 0x01ae 0xfff 0xfff 0xfff syscon nr __NR_thr_exit 0xfff 0xfff 0x01af 0xfff 0xfff 0xfff syscon nr __NR_thr_kill 0xfff 0xfff 0x01b1 0xfff 0xfff 0xfff syscon nr __NR_thr_kill2 0xfff 0xfff 0x01e1 0xfff 0xfff 0xfff -syscon nr __NR_thr_new 0xfff 0xfff 0x01c7 0xfff 0xfff 0xfff syscon nr __NR_thr_self 0xfff 0xfff 0x01b0 0xfff 0xfff 0xfff syscon nr __NR_thr_set_name 0xfff 0xfff 0x01d0 0xfff 0xfff 0xfff syscon nr __NR_thr_suspend 0xfff 0xfff 0x01ba 0xfff 0xfff 0xfff diff --git a/libc/sysv/consts/RLIMIT_AS.S b/libc/sysv/consts/RLIMIT_AS.S index 3a9cdf8e6..6566a9f46 100644 --- a/libc/sysv/consts/RLIMIT_AS.S +++ b/libc/sysv/consts/RLIMIT_AS.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon rlimit,RLIMIT_AS,9,5,10,2,10,9 +.syscon rlimit,RLIMIT_AS,9,5,10,2,10,0 diff --git a/libc/sysv/consts/RLIM_INFINITY.S b/libc/sysv/consts/RLIM_INFINITY.S index 422e8ccc7..dc4f5dd5f 100644 --- a/libc/sysv/consts/RLIM_INFINITY.S +++ b/libc/sysv/consts/RLIM_INFINITY.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon rlim,RLIM_INFINITY,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0 +.syscon rlim,RLIM_INFINITY,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0xffffffffffffffff diff --git a/libc/sysv/consts/RLIM_NLIMITS.S b/libc/sysv/consts/RLIM_NLIMITS.S index ca3b0635d..6d78ad657 100644 --- a/libc/sysv/consts/RLIM_NLIMITS.S +++ b/libc/sysv/consts/RLIM_NLIMITS.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon rlim,RLIM_NLIMITS,16,9,15,9,12,0 +.syscon rlim,RLIM_NLIMITS,16,9,15,9,12,1 diff --git a/libc/sysv/consts/RLIM_SAVED_CUR.S b/libc/sysv/consts/RLIM_SAVED_CUR.S index 78d294007..af38d59b7 100644 --- a/libc/sysv/consts/RLIM_SAVED_CUR.S +++ b/libc/sysv/consts/RLIM_SAVED_CUR.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon rlim,RLIM_SAVED_CUR,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0 +.syscon rlim,RLIM_SAVED_CUR,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0xffffffffffffffff diff --git a/libc/sysv/consts/RLIM_SAVED_MAX.S b/libc/sysv/consts/RLIM_SAVED_MAX.S index 67b4fc5d6..40807baa6 100644 --- a/libc/sysv/consts/RLIM_SAVED_MAX.S +++ b/libc/sysv/consts/RLIM_SAVED_MAX.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon rlim,RLIM_SAVED_MAX,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0 +.syscon rlim,RLIM_SAVED_MAX,0xffffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0x7fffffffffffffff,0xffffffffffffffff diff --git a/libc/sysv/consts/SIGPWR.S b/libc/sysv/consts/SIGPWR.S index 5d15017a3..60286b13b 100644 --- a/libc/sysv/consts/SIGPWR.S +++ b/libc/sysv/consts/SIGPWR.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon compat,SIGPWR,30,30,30,30,32,30 +.syscon sig,SIGPWR,30,30,30,30,32,30 diff --git a/libc/sysv/consts/__NR_thr_new.S b/libc/sysv/consts/__NR_thr_new.S deleted file mode 100644 index f985797f5..000000000 --- a/libc/sysv/consts/__NR_thr_new.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon nr,__NR_thr_new,0xfff,0xfff,0x01c7,0xfff,0xfff,0xfff diff --git a/libc/sysv/consts/nr.h b/libc/sysv/consts/nr.h index 4828240be..00ed55a70 100644 --- a/libc/sysv/consts/nr.h +++ b/libc/sysv/consts/nr.h @@ -697,7 +697,6 @@ extern const long __NR_thr_create; extern const long __NR_thr_exit; extern const long __NR_thr_kill; extern const long __NR_thr_kill2; -extern const long __NR_thr_new; extern const long __NR_thr_self; extern const long __NR_thr_set_name; extern const long __NR_thr_suspend; @@ -1467,7 +1466,6 @@ COSMOPOLITAN_C_END_ #define __NR_thr_exit SYMBOLIC(__NR_thr_exit) #define __NR_thr_kill SYMBOLIC(__NR_thr_kill) #define __NR_thr_kill2 SYMBOLIC(__NR_thr_kill2) -#define __NR_thr_new SYMBOLIC(__NR_thr_new) #define __NR_thr_self SYMBOLIC(__NR_thr_self) #define __NR_thr_set_name SYMBOLIC(__NR_thr_set_name) #define __NR_thr_suspend SYMBOLIC(__NR_thr_suspend) diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 5a1da61b2..20d6d2da4 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -47,9 +47,9 @@ scall sys_lseek 0x0c70c71de20c7008 globl hidden # netbsd+openbsd:evilpad scall __sys_mmap 0x0c50c51dd20c5009 globl hidden # netbsd+openbsd:pad scall sys_msync 0x115100041204101a globl hidden scall sys_mprotect 0x04a04a04a204a00a globl hidden -scall __sys_munmap 0x049049049204090b globl hidden +scall __sys_munmap 0x049049049204900b globl hidden scall sys_sigaction 0x15402e1a0202e00d globl hidden # rt_sigaction on Lunix; it's complicated on NetBSD -scall sys_sigprocmask 0x125030154214900e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask +scall __sys_sigprocmask 0x125030154214900e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask scall sys_ioctl 0x0360360362036010 globl hidden scall sys_pread 0x0ad0ad1db2099011 globl hidden # a.k.a. pread64; netbsd+openbsd:pad scall sys_pwrite 0x0ae0ae1dc209a012 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad @@ -98,7 +98,7 @@ scall __sys_execve 0x03b03b03b203b03b globl hidden scall __sys_wait4 0x1c100b007200703d globl hidden scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu scall sys_killpg 0xffffff092fffffff globl hidden -scall clone 0xfffffffffffff038 globl +scall sys_clone 0x11fffffffffff038 globl hidden scall tkill 0xfffffffffffff0c8 globl scall futex 0xfff053fffffff0ca globl scall set_robust_list 0xfffffffffffff111 globl diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 25115534c..2f5d8f83b 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -18,23 +18,37 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/bits/safemacros.internal.h" +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/log/check.h" +#include "libc/log/color.internal.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" +#include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" +#include "libc/runtime/sysconf.h" +#include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/poll.h" +#include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" #include "third_party/dlmalloc/dlmalloc.h" @@ -81,16 +95,75 @@ void GetOpts(int argc, char *argv[]) { } } +static void EmptySignalMask(void) { + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK, &ss, 0); +} + +static void FixIrregularFds(void) { + int i; + struct pollfd pfds[64]; + for (i = 0; i < 3; ++i) { + if (fcntl(0, F_GETFL) == -1) { + CHECK_EQ(0, open("/dev/null", O_RDWR)); + } + } + for (i = 0; i < ARRAYLEN(pfds); ++i) { + pfds[i].fd = i + 3; + pfds[i].events = POLLIN; + } + if (poll(pfds, ARRAYLEN(pfds), 0) != -1) { + for (i = 0; i < ARRAYLEN(pfds); ++i) { + if (pfds[i].revents & POLLNVAL) continue; + CHECK_EQ(0, close(pfds[i].fd)); + } + } +} + +static void SetLimit(int resource, uint64_t soft, uint64_t hard) { + struct rlimit old; + struct rlimit lim = {soft, hard}; + if (resource == 127) return; + if (setrlimit(resource, &lim) == -1) { + if (!getrlimit(resource, &old)) { + lim.rlim_max = MIN(hard, old.rlim_max); + lim.rlim_cur = MIN(soft, lim.rlim_max); + setrlimit(resource, &lim); + } + } +} + /** * Generic test program main function. */ noasan int main(int argc, char *argv[]) { + unsigned cpus; const char *comdbg; __log_level = kLogInfo; GetOpts(argc, argv); + + // normalize this process + FixIrregularFds(); + EmptySignalMask(); ShowCrashReports(); + + // so the test runner can terminate unknown children without + // accidentally killing parent processes + if (!IsWindows() && weaken(fork)) { + setpgrp(); + } + + // prevent runaway tests from bombing the computer + cpus = GetCpuCount(); + cpus = MAX(4, cpus); + SetLimit(RLIMIT_NOFILE, 32, 128); + SetLimit(RLIMIT_SIGPENDING, 16, 16384); + SetLimit(RLIMIT_NPROC, cpus * 8, 2048); + + // now get down to business g_testlib_shoulddebugbreak = IsDebuggerPresent(false); - if (!IsWindows()) sys_getpid(); /* make strace easier to read */ + if (!IsWindows()) sys_getpid(); // make strace easier to read testlib_clearxmmregisters(); testlib_runalltests(); if (!g_testlib_failed && runbenchmarks_ && weaken(testlib_runallbenchmarks)) { @@ -99,10 +172,12 @@ noasan int main(int argc, char *argv[]) { CheckForMemoryLeaks(); } if (!g_testlib_failed && IsRunningUnderMake()) { - return 254; /* compile.com considers this 0 and propagates output */ + return 254; // compile.com considers this 0 and propagates output } } else if (!g_testlib_failed) { CheckForMemoryLeaks(); } + + // we're done! exit(min(255, g_testlib_failed)); } diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 3dddd4dae..d1ce3f6fb 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -16,29 +16,39 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" +#include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/nt/process.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" +#include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/poll.h" +#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/w.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" static int x; char g_testlib_olddir[PATH_MAX]; char g_testlib_tmpdir[PATH_MAX]; +struct sigaction wanthandlers[31]; void testlib_finish(void) { if (g_testlib_failed) { @@ -61,7 +71,6 @@ static void SetupTmpDir(void) { p = FormatInt64(p, x++); p[0] = '\0'; CHECK_NE(-1, makedirs(g_testlib_tmpdir, 0755), "%s", g_testlib_tmpdir); - CHECK_EQ(1, isdirectory(g_testlib_tmpdir), "%s", g_testlib_tmpdir); CHECK_NOTNULL(realpath(g_testlib_tmpdir, g_testlib_tmpdir), "%`'s", g_testlib_tmpdir); CHECK_NE(-1, chdir(g_testlib_tmpdir), "%s", g_testlib_tmpdir); @@ -69,8 +78,93 @@ static void SetupTmpDir(void) { static void TearDownTmpDir(void) { CHECK_NE(-1, chdir(g_testlib_olddir)); - CHECK_NE(-1, rmrf(g_testlib_tmpdir), "ugh %s", g_testlib_tmpdir); - CHECK_EQ(0, isdirectory(g_testlib_tmpdir), "%s", g_testlib_tmpdir); + CHECK_NE(-1, rmrf(g_testlib_tmpdir), "%s", g_testlib_tmpdir); +} + +static void DoNothing(int sig) { + // function intentionally empty +} + +static void CopySignalHandlers(void) { +#if 0 + int i; + for (i = 0; i < ARRAYLEN(wanthandlers); ++i) { + if (i + 1 == SIGKILL || i + 1 == SIGSTOP) continue; + CHECK_EQ(0, sigaction(i + 1, 0, wanthandlers + i), "sig=%d", i + 1); + } +#endif +} + +static void CheckSignalHandler(int sig) { +#if 0 + int i; + struct sigaction sa = {0}; + assert(0 <= sig - 1 && sig - 1 < ARRAYLEN(wanthandlers)); + CHECK_EQ(0, sigaction(sig, 0, &sa)); + CHECK_EQ(0, memcmp(wanthandlers + sig - 1, &sa, sizeof(sa)), + "signal handler for %s was %p/%#x/%#x:%x " + "but should have been restored to %p/%#x/%#x:%x", + strsignal(sig), sa.sa_handler, sa.sa_flags, sa.sa_mask.__bits[0], + sa.sa_mask.__bits[1], wanthandlers[sig - 1].sa_handler, + wanthandlers[sig - 1].sa_flags, + wanthandlers[sig - 1].sa_mask.__bits[0], + wanthandlers[sig - 1].sa_mask.__bits[1]); +#endif +} + +static void CheckForSignalHandlers(void) { +#if 0 + CheckSignalHandler(SIGINT); + CheckSignalHandler(SIGQUIT); + CheckSignalHandler(SIGCHLD); + CheckSignalHandler(SIGFPE); + CheckSignalHandler(SIGILL); + CheckSignalHandler(SIGSEGV); + CheckSignalHandler(SIGTRAP); + CheckSignalHandler(SIGABRT); + CheckSignalHandler(SIGBUS); + CheckSignalHandler(SIGSYS); + CheckSignalHandler(SIGWINCH); +#endif +} + +static void CheckForFileDescriptors(void) { +#if 0 + // TODO: race condition on fd cleanup :'( + int i; + struct pollfd pfds[16]; + if (!weaken(open) && !weaken(socket)) return; + for (i = 0; i < ARRAYLEN(pfds); ++i) { + pfds[i].fd = i + 3; + pfds[i].events = POLLIN; + } + if (poll(pfds, ARRAYLEN(pfds), 0) > 0) { + for (i = 0; i < ARRAYLEN(pfds); ++i) { + if (pfds[i].revents & POLLNVAL) continue; + ++g_testlib_failed; + fprintf(stderr, "error: test failed to close() fd %d%n", pfds[i].fd); + } + } +#endif +} + +static void CheckForZombies(void) { +#if 0 + int e, pid; + sigset_t ss, oldss; + struct sigaction oldsa; + struct sigaction ignore = {.sa_handler = DoNothing}; + if (!weaken(fork)) return; + for (;;) { + if ((pid = wait(0)) == -1) { + CHECK_EQ(ECHILD, errno); + break; + } else { + ++g_testlib_failed; + fprintf(stderr, "error: test failed to reap zombies %d%n", pid); + } + } +#endif } /** @@ -94,6 +188,7 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, * @see ape/ape.lds */ const testfn_t *fn; + CopySignalHandlers(); CHECK_NOTNULL(getcwd(g_testlib_olddir, sizeof(g_testlib_olddir))); if (weaken(testlib_enable_tmp_setup_teardown_once)) SetupTmpDir(); if (weaken(SetUpOnce)) weaken(SetUpOnce)(); @@ -109,6 +204,9 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, if (!IsWindows()) sys_getpid(); if (weaken(TearDown)) weaken(TearDown)(); if (weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir(); + CheckForFileDescriptors(); + CheckForSignalHandlers(); + CheckForZombies(); } if (weaken(TearDownOnce)) weaken(TearDownOnce)(); if (weaken(testlib_enable_tmp_setup_teardown_once)) TearDownTmpDir(); diff --git a/libc/thread/clone.c b/libc/thread/clone.c new file mode 100644 index 000000000..616f63712 --- /dev/null +++ b/libc/thread/clone.c @@ -0,0 +1,395 @@ +/*-*- 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/asmflag.h" +#include "libc/bits/weaken.h" +#include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/ucontext-netbsd.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/spinlock.h" +#include "libc/intrin/tls.h" +#include "libc/intrin/winthread.internal.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/nt2sysv.h" +#include "libc/nexgen32e/stackframe.h" +#include "libc/nt/runtime.h" +#include "libc/nt/thread.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/clone.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/errfuns.h" +#include "libc/thread/freebsd.internal.h" +#include "libc/thread/openbsd.internal.h" + +// TODO(jart): work in progress + +STATIC_YOINK("gettid"); // for kprintf() + +#define __NR_thr_new 455 +#define __NR_sys___tfork 8 +#define __NR_clone_linux 56 +#define __NR__lwp_create 309 +#define __NR_getcontext_netbsd 307 +#define __NR__lwp_setprivate 317 + +extern bool __threaded; + +static struct Cloner { + _Alignas(64) char lock; + _Alignas(64) int flags; + int64_t tid; + int (*func)(void *); + void *arg; + void *stack; + int *ctid; + int *ptid; +} __cloner; + +static textwindows uint32_t WinThreadMain(void *notused) { + int (*func)(void *); + void *arg, *stack; + struct WinThread *wt; + int exitcode, tid, flags, *ctid; + tid = __cloner.tid; + arg = __cloner.arg; + func = __cloner.func; + ctid = __cloner.ctid; + flags = __cloner.flags; + stack = __cloner.stack; + _spunlock(&__cloner.lock); + wt = calloc(1, sizeof(struct WinThread)); + wt->pid = tid; + TlsSetValue(__winthread, wt); + if (flags & CLONE_CHILD_SETTID) *ctid = tid; + asm volatile("mov\t%%rbp,%%rbx\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"); + if (flags & CLONE_CHILD_CLEARTID) *ctid = 0; + __releasefd(tid); + free(wt); + return exitcode; +} + +static textwindows int CloneWindows(int (*func)(void *), void *stk, + size_t stksz, int flags, void *arg, + int *ptid, void *tls, size_t tlssz, + int *ctid) { + int tid; + int64_t hand; + uint32_t wintid; + if ((tid = __reservefd(-1)) == -1) return -1; + _spinlock(&__cloner.lock); + __cloner.tid = tid; + __cloner.arg = arg; + __cloner.func = func; + __cloner.ctid = ctid; + __cloner.flags = flags; + __cloner.stack = (char *)stk + stksz; + if (!(hand = CreateThread(&kNtIsInheritable, 0, NT2SYSV(WinThreadMain), 0, 0, + &wintid))) { + _spunlock(&__cloner.lock); + return -1; + } + if (flags & CLONE_CHILD_SETTID) *ctid = tid; + if (flags & CLONE_PARENT_SETTID) *ptid = tid; + // XXX: this should be tracked in a separate data structure + g_fds.p[tid].kind = kFdProcess; + g_fds.p[tid].handle = hand; + g_fds.p[tid].flags = O_CLOEXEC; + g_fds.p[tid].zombie = false; + return tid; +} + +static dontinline wontreturn void BsdThreadMain(void *unused) { + void *arg; + int (*func)(void *); + int tid, flags, exitcode, *ctid; + tid = __cloner.tid; + arg = __cloner.arg; + func = __cloner.func; + ctid = __cloner.ctid; + flags = __cloner.flags; + _spunlock(&__cloner.lock); + if (flags & CLONE_CHILD_SETTID) *ctid = tid; + exitcode = func(arg); + if (flags & CLONE_CHILD_CLEARTID) *ctid = 0; + _Exit1(exitcode); +} + +static privileged noasan int CloneFreebsd(int (*func)(void *), void *stk, + size_t stksz, int flags, void *arg, + int *ptid, void *tls, size_t tlssz, + int *ctid) { + int ax; + bool failed; + int64_t tid; + struct thr_param params = {0}; + _spinlock(&__cloner.lock); + __cloner.arg = arg; + __cloner.func = func; + __cloner.ctid = ctid; + __cloner.flags = flags; + params.start_func = BsdThreadMain; + params.stack_base = stk; + params.stack_size = stksz; + params.tls_base = flags & CLONE_SETTLS ? tls : 0; + params.tls_size = flags & CLONE_SETTLS ? tlssz : 0; + params.child_tid = &__cloner.tid; + params.parent_tid = &tid; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax) + : "1"(__NR_thr_new), "D"(¶ms), "S"(sizeof(params)) + : "rcx", "r11", "memory", "cc"); + if (!failed) { + if (flags & CLONE_PARENT_SETTID) *ptid = tid; + return tid; + } else { + errno = ax; + return -1; + } +} + +static privileged noasan int CloneOpenbsd(int (*func)(void *), char *stk, + size_t stksz, int flags, void *arg, + int *ptid, void *tls, size_t tlssz, + int *ctid) { + int ax; + bool failed; + struct __tfork params; + _spinlock(&__cloner.lock); + __cloner.arg = arg; + __cloner.func = func; + __cloner.ctid = ctid; + __cloner.flags = flags; + __cloner.tid = 0; + asm volatile("" ::: "memory"); + params.tf_tid = (int *)&__cloner.tid; + params.tf_tcb = flags & CLONE_SETTLS ? tls : 0; + params.tf_stack = stk + stksz; + 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 { + errno = ax; + return -1; + } +} + +static privileged noasan int CloneNetbsd(int (*func)(void *), void *stk, + size_t stksz, int flags, void *arg, + int *ptid, void *tls, size_t tlssz, + int *ctid) { + int ax, tid; + bool failed; + intptr_t *stack; + struct ucontext_netbsd ctx; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax) + : "1"(__NR_getcontext_netbsd), "D"(&ctx) + : "rcx", "r11", "memory", "cc"); + if (failed) { + errno = ax; + return -1; + } + stack = (void *)(((long)((char *)stk + stksz) & -16) - 8 * 3); + *(long *)stack = (long)_Exit1; + ctx.uc_link = 0; + ctx.uc_mcontext.rip = (intptr_t)func; + ctx.uc_mcontext.rdi = (intptr_t)arg; + ctx.uc_mcontext.rsp = (intptr_t)stack; + ctx.uc_mcontext.rbp = 0; + ctx.uc_flags |= _UC_STACK; + ctx.uc_stack.ss_sp = stk; + ctx.uc_stack.ss_size = stksz; + ctx.uc_stack.ss_flags = 0; + if (flags & CLONE_SETTLS) { + ctx.uc_flags |= _UC_TLSBASE; + ctx.uc_mcontext._mc_tlsbase = (intptr_t)tls; + } + asm volatile("" ::: "memory"); + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax) + : "1"(__NR__lwp_create), "D"(&ctx), "S"(flags), "d"(&tid) + : "rcx", "r11", "memory", "cc"); + if (failed) { + errno = ax; + return -1; + } + if (flags & CLONE_PARENT_SETTID) *ptid = ax; + if (flags & CLONE_CHILD_SETTID) *ctid = ax; + return tid; +} + +static privileged int CloneLinux(int (*func)(void *), void *stk, size_t stksz, + int flags, void *arg, int *ptid, void *tls, + size_t tlssz, int *ctid) { + int ax; + bool failed; + 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; + asm volatile("syscall" + : "=a"(ax) + : "0"(__NR_clone_linux), "D"(flags), "S"(stk), "d"(ptid), + "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + if (ax > -4096u) { + errno = -ax; + return -1; + } + if (ax) return ax; + asm volatile("xor\t%%ebp,%%ebp\n\t" + "pop\t%%rdi\n\t" + "call\t%0\n\t" + "xchg\t%%eax,%%edi\n\t" + "jmp\t_Exit1" + : /* no outputs */ + : "r"(r9) + : "memory"); + unreachable; +} + +/** + * Creates thread. + * + * This function follows the same ABI convention as the Linux userspace + * libraries, with a few small changes. The varargs has been removed to + * help prevent broken code, and the stack size and tls size parameters + * are introduced for compatibility with FreeBSD. + * + * @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 + * @param stksz is the size of that stack in bytes which must be zero + * if the fork() or vfork() equivalent flags are used it's highly + * recommended that this value be GetStackSize(), or else kprintf + * and other runtime services providing memory safety can't do as + * good and quick of a job at that + * @param flags usually has one of + * - `SIGCHLD` will delegate to fork() + * - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork() + * - `CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` for threads + * as part high bytes, and the low order byte may optionally contain + * a signal e.g. SIGCHLD, to enable parent notification on terminate + * although the signal isn't supported on non-Linux and non-NetBSD + * at the moment; 'flags' may optionally bitwise or the following: + * - `CLONE_PARENT_SETTID` is needed for `ctid` should be set + * - `CLONE_CHILD_SETTID` is needed for `ptid` should be set + * - `CLONE_SETTLS` is needed to set `%fs` segment to `tls` + * @param arg will be passed to your callback + * @param ptid lets the parent receive the child thread id; + * this parameter is ignored if `CLONE_PARENT_SETTID` is not set + * @param tls may be used to set the thread local storage segment; + * this parameter is ignored if `CLONE_SETTLS` is not set + * @param tlssz is the size of tls in bytes + * @param ctid lets the child receive its thread id; + * this parameter is ignored if `CLONE_CHILD_SETTID` is not set + * @return tid on success and 0 to the child, otherwise -1 w/ errno + * @returnstwice + * @threadsafe + */ +privileged int clone(int (*func)(void *), void *stk, size_t stksz, int flags, + void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) { + int rc; + __threaded = true; + if (IsAsan() && + (!__asan_is_valid(stk, stksz) || + ((flags & CLONE_SETTLS) && !__asan_is_valid(tls, tlssz)) || + ((flags & CLONE_SETTLS) && !__asan_is_valid(tls, sizeof(long))) || + ((flags & CLONE_PARENT_SETTID) && + !__asan_is_valid(ptid, sizeof(*ptid))) || + ((flags & CLONE_CHILD_SETTID) && + !__asan_is_valid(ctid, sizeof(*ctid))))) { + rc = efault(); + } else if (IsLinux()) { + rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + } else if (IsNetbsd()) { + rc = CloneNetbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + } + + // polyfill fork() and vfork() use case on platforms w/o clone + else if (flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) { + if (IsTiny()) { + rc = einval(); + } else if (!arg && !stksz) { + return vfork(); // don't log clone() + } else { + rc = einval(); + } + } else if (flags == SIGCHLD) { + if (IsTiny()) { + rc = eopnotsupp(); + } else if (!arg && !stksz) { + return fork(); // don't log clone() + } else { + rc = einval(); + } + } + + // we now assume we're creating a thread + // these platforms can't do signals the way linux does + else if ((flags & + ~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) != + (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) { + rc = einval(); + } else if (IsFreebsd()) { + rc = CloneFreebsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + } else if (IsOpenbsd()) { + rc = CloneOpenbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + } + + // These platforms can't do segment registers like linux does + else if (flags & CLONE_SETTLS) { + rc = einval(); + } else if (IsWindows()) { + rc = CloneWindows(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid); + } else { + rc = enosys(); + } + + STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d% m", func, stk, + stksz, flags, arg, ptid, tls, tlssz, ctid, rc); + return rc; +} diff --git a/libc/thread/freebsd.internal.h b/libc/thread/freebsd.internal.h index 3618f2c3d..e92180099 100644 --- a/libc/thread/freebsd.internal.h +++ b/libc/thread/freebsd.internal.h @@ -31,15 +31,6 @@ struct thr_param { struct rtprio *rtp; }; -static inline wontreturn void thr_exit(int64_t *opt_out_state) { - long ax, di; - asm volatile("syscall" - : "=a"(ax) - : "0"(431), "D"(opt_out_state) - : "memory", "cc"); - unreachable; -} - static inline int thr_new(struct thr_param *param, int param_size) { bool failed; long ax, di, si; @@ -51,62 +42,6 @@ static inline int thr_new(struct thr_param *param, int param_size) { return ax; } -static inline int thr_kill(int64_t id, int sig) { - bool failed; - long ax, di, si; - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si) - : "1"(433), "2"(id), "3"(sig) - : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); - if (failed) ax = -ax; - return ax; -} - -static inline int thr_suspend(const struct timespec *opt_timeout) { - bool failed; - long ax, di; - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di) - : "1"(442), "2"(opt_timeout) - : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory"); - if (failed) ax = -ax; - return ax; -} - -static inline int thr_wake(int64_t id) { - bool failed; - long ax, di; - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di) - : "1"(443), "2"(id) - : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory"); - if (failed) ax = -ax; - return ax; -} - -static inline int thr_set_name(int64_t id, const char *name) { - bool failed; - long ax, di, si; - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si) - : "1"(464), "2"(id), "3"(name) - : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); - if (failed) ax = -ax; - return ax; -} - -static inline int thr_kill2(int pid, int64_t id, int sig) { - bool failed; - long ax, di, si, dx; - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si), - "=d"(dx) - : "1"(481), "2"(pid), "3"(id), "4"(sig) - : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (failed) ax = -ax; - return ax; -} - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */ diff --git a/libc/thread/openbsd.internal.h b/libc/thread/openbsd.internal.h new file mode 100644 index 000000000..2c89f5d4f --- /dev/null +++ b/libc/thread/openbsd.internal.h @@ -0,0 +1,14 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct __tfork { + void *tf_tcb; + int32_t *tf_tid; + void *tf_stack; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_ */ diff --git a/libc/thread/thread.mk b/libc/thread/thread.mk index 137c3e001..ee0d6ddb1 100644 --- a/libc/thread/thread.mk +++ b/libc/thread/thread.mk @@ -28,6 +28,7 @@ LIBC_THREAD_A_DIRECTDEPS = \ LIBC_CALLS \ LIBC_INTRIN \ LIBC_BITS \ + LIBC_MEM \ LIBC_RUNTIME \ LIBC_SYSV \ LIBC_SYSV_CALLS \ @@ -36,7 +37,8 @@ LIBC_THREAD_A_DIRECTDEPS = \ LIBC_THREAD_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)))) -$(LIBC_THREAD_A): libc/thread/ \ +$(LIBC_THREAD_A): \ + libc/thread/ \ $(LIBC_THREAD_A).pkg \ $(LIBC_THREAD_A_OBJS) @@ -44,6 +46,10 @@ $(LIBC_THREAD_A).pkg: \ $(LIBC_THREAD_A_OBJS) \ $(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg) +o/$(MODE)/libc/thread/clone.o: \ + OVERRIDE_CFLAGS += \ + -mno-red-zone + LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x))) LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS)) LIBC_THREAD_HDRS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/x/xslurp.c b/libc/x/xslurp.c index a99bb093e..b3b137a60 100644 --- a/libc/x/xslurp.c +++ b/libc/x/xslurp.c @@ -32,22 +32,21 @@ */ void *xslurp(const char *path, size_t *opt_out_size) { int fd; - ssize_t rc; size_t i, got; char *res, *p; - struct stat st; + ssize_t rc, size; res = NULL; if ((fd = open(path, O_RDONLY)) != -1) { - if (fstat(fd, &st) != -1 && (res = valloc(st.st_size + 1))) { - if (st.st_size > 2 * 1024 * 1024) { - fadvise(fd, 0, st.st_size, MADV_SEQUENTIAL); + if ((size = getfiledescriptorsize(fd)) != -1 && (res = valloc(size + 1))) { + if (size > 2 * 1024 * 1024) { + fadvise(fd, 0, size, MADV_SEQUENTIAL); } - for (i = 0; i < st.st_size; i += got) { + for (i = 0; i < size; i += got) { TryAgain: - if ((rc = pread(fd, res + i, st.st_size - i, i)) != -1) { + if ((rc = pread(fd, res + i, size - i, i)) != -1) { if (!(got = rc)) { - if (fstat(fd, &st) == -1) { - abort(); + if (getfiledescriptorsize(fd) == -1) { + abort(); // TODO(jart): what is this } } } else if (errno == EINTR) { @@ -60,7 +59,7 @@ void *xslurp(const char *path, size_t *opt_out_size) { } if (res) { if (opt_out_size) { - *opt_out_size = st.st_size; + *opt_out_size = size; } res[i] = '\0'; } diff --git a/libc/zip.h b/libc/zip.h index 250438e0d..038cac34c 100644 --- a/libc/zip.h +++ b/libc/zip.h @@ -200,7 +200,6 @@ uint64_t GetZipCfileCompressedSize(const uint8_t *); uint64_t GetZipCfileOffset(const uint8_t *); uint64_t GetZipLfileUncompressedSize(const uint8_t *); uint64_t GetZipLfileCompressedSize(const uint8_t *); -uint8_t *zipfindcentraldir(const uint8_t *, size_t); void GetZipCfileTimestamps(const uint8_t *, struct timespec *, struct timespec *, struct timespec *, int); diff --git a/libc/zipos/get.c b/libc/zipos/get.c index c92d238a4..93a067eeb 100644 --- a/libc/zipos/get.c +++ b/libc/zipos/get.c @@ -18,12 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/mem/alloca.h" @@ -60,19 +62,28 @@ static void __zipos_munmap_unneeded(const uint8_t *base, const uint8_t *cdir, /** * Returns pointer to zip central directory of current executable. + * @asyncsignalsafe (TODO: verify this) + * @threadsafe */ struct Zipos *__zipos_get(void) { - static bool once; - static struct Zipos zipos; int fd; char *path; - size_t size; + ssize_t size; + static bool once; sigset_t neu, old; + struct Zipos *res; + const char *progpath; + static struct Zipos zipos; uint8_t *map, *base, *cdir; + _Alignas(64) static char lock; + _spinlock(&lock); if (!once) { sigfillset(&neu); - sigprocmask(SIG_BLOCK, &neu, &old); - if ((fd = open(GetProgramExecutableName(), O_RDONLY)) != -1) { + if (!IsWindows()) { + sys_sigprocmask(SIG_BLOCK, &neu, &old); + } + progpath = GetProgramExecutableName(); + if ((fd = open(progpath, O_RDONLY)) != -1) { if ((size = getfiledescriptorsize(fd)) != SIZE_MAX && (map = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) { if ((base = FindEmbeddedApe(map, size))) { @@ -84,18 +95,26 @@ struct Zipos *__zipos_get(void) { __zipos_munmap_unneeded(base, cdir, map); zipos.map = base; zipos.cdir = cdir; - STRACE("__zipos_get(%#s)", program_executable_name); + STRACE("__zipos_get(%#s)", progpath); } else { munmap(map, size); - STRACE("__zipos_get(%#s) → eocd not found", program_executable_name); + STRACE("__zipos_get(%#s) → eocd not found", progpath); } } close(fd); } else { - STRACE("__zipos_get(%#s) → open failed %m", program_executable_name); + STRACE("__zipos_get(%#s) → open failed %m", progpath); + } + if (!IsWindows()) { + sigprocmask(SIG_SETMASK, &old, 0); } once = true; - sigprocmask(SIG_SETMASK, &old, 0); } - return zipos.cdir ? &zipos : 0; + if (zipos.cdir) { + res = &zipos; + } else { + res = 0; + } + _spunlock(&lock); + return res; } diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index e69dc2b62..88e101a7a 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -36,6 +36,7 @@ TEST(access, efault) { } TEST(access, enoent) { + ASSERT_SYS(ENOENT, -1, access("", F_OK)); ASSERT_SYS(ENOENT, -1, access("doesnotexist", F_OK)); ASSERT_SYS(ENOENT, -1, access("o/doesnotexist", F_OK)); } diff --git a/test/libc/calls/chdir_test.c b/test/libc/calls/chdir_test.c index 8c91665e9..4fa925296 100644 --- a/test/libc/calls/chdir_test.c +++ b/test/libc/calls/chdir_test.c @@ -30,6 +30,7 @@ TEST(chdir, efault) { } TEST(chdir, enoent) { + ASSERT_SYS(ENOENT, -1, chdir("")); ASSERT_SYS(ENOENT, -1, chdir("doesnotexist")); ASSERT_SYS(ENOENT, -1, chdir("o/doesnotexist")); } diff --git a/test/libc/calls/fork_test.c b/test/libc/calls/fork_test.c index d70f93207..53e6c9a74 100644 --- a/test/libc/calls/fork_test.c +++ b/test/libc/calls/fork_test.c @@ -22,6 +22,7 @@ #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/prot.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" TEST(fork, testPipes) { @@ -77,3 +78,16 @@ TEST(fork, testSharedMemory) { EXPECT_NE(-1, munmap(sharedvar, FRAMESIZE)); EXPECT_NE(-1, munmap(privatevar, FRAMESIZE)); } + +void ForkInSerial(void) { + int pid, ws; + ASSERT_NE(-1, (pid = fork())); + if (!pid) _Exit(0); + ASSERT_NE(-1, waitpid(pid, &ws, 0)); + ASSERT_TRUE(WIFEXITED(ws)); + ASSERT_EQ(0, WEXITSTATUS(ws)); +} + +BENCH(fork, bench) { + EZBENCH2("fork", donothing, ForkInSerial()); +} diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index cc6852d77..504123a64 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -31,6 +31,7 @@ TEST(open, efault) { } TEST(open, enoent) { + ASSERT_SYS(ENOENT, -1, open("", O_RDONLY)); ASSERT_SYS(ENOENT, -1, open("doesnotexist", O_RDONLY)); ASSERT_SYS(ENOENT, -1, open("o/doesnotexist", O_RDONLY)); } diff --git a/test/libc/calls/pipe_test.c b/test/libc/calls/pipe_test.c new file mode 100644 index 000000000..c88c5d0f2 --- /dev/null +++ b/test/libc/calls/pipe_test.c @@ -0,0 +1,75 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/consts/rlimit.h" +#include "libc/testlib/testlib.h" + +int ws, pid, f[2]; +char buf[6] = {0}; +struct rlimit rlim = {0, 10}; + +TEST(pipe, efault) { + EXPECT_SYS(EFAULT, -1, pipe(0)); +} + +TEST(pipe, einval) { + EXPECT_SYS(EINVAL, -1, pipe2(f, -1)); +} + +TEST(pipe, ebadf) { + if (IsFreebsd()) return; // somehow succeeds + if (IsOpenbsd()) return; // somehow succeeds + EXPECT_SYS(0, 0, pipe(f)); + EXPECT_SYS(EBADF, -1, write(f[0], "h", 1)); + EXPECT_SYS(EBADF, -1, read(f[1], buf, 1)); + EXPECT_SYS(0, 0, close(f[0])); + EXPECT_SYS(0, 0, close(f[1])); +} + +TEST(pipe, emfile) { + if (IsWindows()) return; // TODO + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlim)); + ASSERT_SYS(EMFILE, -1, pipe(f)); + _exit(0); + } + EXPECT_NE(-1, waitpid(pid, &ws, 0)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} + +TEST(pipe, usesLowestFileNumbers) { + EXPECT_SYS(0, 0, pipe(f)); + EXPECT_SYS(0, 3, f[0]); + EXPECT_SYS(0, 4, f[1]); + EXPECT_SYS(0, 0, close(f[0])); + EXPECT_SYS(0, 0, close(f[1])); +} + +TEST(pipe, doesBuffering) { + EXPECT_SYS(0, 0, pipe(f)); + EXPECT_SYS(0, 5, write(f[1], "hello", 5)); + EXPECT_SYS(0, 5, read(f[0], buf, 5)); + EXPECT_STREQ("hello", buf); + EXPECT_SYS(0, 0, close(f[0])); + EXPECT_SYS(0, 0, close(f[1])); +} diff --git a/test/libc/calls/readansi_test.c b/test/libc/calls/readansi_test.c index 4c4b10d60..12f4c6e41 100644 --- a/test/libc/calls/readansi_test.c +++ b/test/libc/calls/readansi_test.c @@ -52,6 +52,7 @@ TEST(readansi, test) { EXPECT_STREQ("\xc2\x9bM", b); EXPECT_EQ(0, readansi(fds[0], b, sizeof(b))); EXPECT_STREQ("", b); + close(fds[0]); ASSERT_NE(-1, wait(&ws)); ASSERT_TRUE(WIFEXITED(ws)); ASSERT_EQ(0, WEXITSTATUS(ws)); @@ -73,6 +74,7 @@ TEST(readansi, testOperatingSystemCommand) { EXPECT_STREQ(s, b); EXPECT_EQ(0, readansi(fds[0], b, sizeof(b))); EXPECT_STREQ("", b); + close(fds[0]); ASSERT_NE(-1, wait(&ws)); ASSERT_TRUE(WIFEXITED(ws)); ASSERT_EQ(0, WEXITSTATUS(ws)); diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index 5f9affb24..add8e75aa 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -31,6 +31,18 @@ char testlib_enable_tmp_setup_teardown; +TEST(readlink, enoent) { + char buf[32]; + ASSERT_SYS(ENOENT, -1, readlink("doesnotexist", buf, 32)); + ASSERT_SYS(ENOENT, -1, readlink("o/doesnotexist", buf, 32)); +} + +TEST(readlink, enotdir) { + char buf[32]; + ASSERT_SYS(0, 0, touch("o", 0644)); + ASSERT_SYS(ENOTDIR, -1, readlink("o/doesnotexist", buf, 32)); +} + TEST(readlinkat, test) { char buf[128], *p, *q; memset(buf, -1, sizeof(buf)); diff --git a/test/libc/calls/renameat_test.c b/test/libc/calls/renameat_test.c index 51c8ec228..658ee52ba 100644 --- a/test/libc/calls/renameat_test.c +++ b/test/libc/calls/renameat_test.c @@ -24,6 +24,20 @@ char testlib_enable_tmp_setup_teardown; +TEST(rename, enoent) { + EXPECT_SYS(ENOENT, -1, rename("foo", "")); + EXPECT_SYS(ENOENT, -1, rename("", "foo")); + EXPECT_SYS(ENOENT, -1, rename("foo", "o/bar")); + EXPECT_SYS(ENOENT, -1, rename("o/bar", "foo")); +} + +TEST(renameat, enotdir) { + EXPECT_SYS(0, 0, close(creat("yo", 0644))); + EXPECT_SYS(ENOTDIR, -1, rename("yo/there", "hrcue")); + // this test makes platforms crazy + // EXPECT_SYS(ENOTDIR, -1, rename("zoo", "yo/there")); +} + TEST(renameat, testNull_returnsEfault) { ASSERT_SYS(0, 0, close(creat("hello", 0644))); EXPECT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, 0)); diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index c51f5f8ad..b32ed2306 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -43,6 +43,7 @@ TEST(stat_010, testEmptyFile_sizeIsZero) { TEST(stat, enoent) { ASSERT_SYS(ENOENT, -1, stat("hi", 0)); + ASSERT_SYS(ENOENT, -1, stat("o/doesnotexist", 0)); } TEST(stat, enotdir) { diff --git a/test/libc/calls/symlinkat_test.c b/test/libc/calls/symlinkat_test.c index 821cd50c9..c8d06f68e 100644 --- a/test/libc/calls/symlinkat_test.c +++ b/test/libc/calls/symlinkat_test.c @@ -29,6 +29,16 @@ char testlib_enable_tmp_setup_teardown; char p[2][PATH_MAX]; struct stat st; +TEST(symlink, enoent) { + ASSERT_SYS(ENOENT, -1, symlink("o/foo", "")); + ASSERT_SYS(ENOENT, -1, symlink("o/foo", "o/bar")); +} + +TEST(symlinkat, enotdir) { + ASSERT_SYS(0, 0, close(creat("yo", 0644))); + ASSERT_SYS(ENOTDIR, -1, symlink("hrcue", "yo/there")); +} + TEST(symlinkat, test) { sprintf(p[0], "%s.%d", program_invocation_short_name, rand()); sprintf(p[1], "%s.%d", program_invocation_short_name, rand()); diff --git a/test/libc/calls/unlinkat_test.c b/test/libc/calls/unlinkat_test.c index 280d8ad87..b744ce80b 100644 --- a/test/libc/calls/unlinkat_test.c +++ b/test/libc/calls/unlinkat_test.c @@ -17,11 +17,29 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/sysv/consts/at.h" #include "libc/testlib/testlib.h" char testlib_enable_tmp_setup_teardown; +TEST(unlink, efault) { + ASSERT_SYS(EFAULT, -1, unlink(0)); + if (IsWindows() && !IsAsan()) return; // not possible + ASSERT_SYS(EFAULT, -1, unlink((void *)77)); +} + +TEST(unlink, enoent) { + ASSERT_SYS(ENOENT, -1, unlink("")); + ASSERT_SYS(ENOENT, -1, unlink("doesnotexist")); + ASSERT_SYS(ENOENT, -1, unlink("o/doesnotexist")); +} + +TEST(unlink, enotdir) { + ASSERT_SYS(0, 0, touch("o", 0644)); + ASSERT_SYS(ENOTDIR, -1, unlink("o/doesnotexist")); +} + TEST(unlinkat, test) { int i, fd; EXPECT_EQ(0, touch("mytmp", 0644)); diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index f6a64148f..eb8ab23cd 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -196,6 +196,7 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(78, WEXITSTATUS(ws)); @@ -273,6 +274,7 @@ TEST(ShowCrashReports, testStackOverrunCrash) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(77, WEXITSTATUS(ws)); @@ -381,6 +383,7 @@ TEST(ShowCrashReports, testDivideByZero) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); assert(128 + SIGFPE == WEXITSTATUS(ws) || 77 == WEXITSTATUS(ws)); @@ -502,6 +505,7 @@ TEST(ShowCrashReports, testBssOverrunCrash) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(77, WEXITSTATUS(ws)); @@ -580,6 +584,7 @@ TEST(ShowCrashReports, testNpeCrash) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(77, WEXITSTATUS(ws)); @@ -639,6 +644,7 @@ TEST(ShowCrashReports, testDataOverrunCrash) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(77, WEXITSTATUS(ws)); @@ -693,6 +699,7 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { break; } } + close(fds[0]); ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(IsAsan() ? 77 : 128 + SIGSEGV, WEXITSTATUS(ws)); diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index d71e88e7d..50672b6cc 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -22,8 +22,10 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/rand/rand.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/sa.h" @@ -33,6 +35,7 @@ #define THREADS 8 #define ENTRIES 256 +char locks[THREADS]; volatile bool ready; volatile uint64_t A[THREADS * ENTRIES]; @@ -48,6 +51,7 @@ int Thrasher(void *arg) { for (i = 0; i < ENTRIES; ++i) { A[id * ENTRIES + i] = rand64(); } + _spunlock(locks + id); return 0; } @@ -69,8 +73,10 @@ TEST(rand64, testLcg_doesntProduceIdenticalValues) { TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { char *stack; sigset_t ss, oldss; - int i, j, rc, ws, tid[THREADS], ptid[THREADS], ctid[THREADS], tls[THREADS]; - if (!IsLinux() && !IsNetbsd()) return; + 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)); @@ -78,22 +84,23 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { sigemptyset(&ss); sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); + for (i = 0; i < THREADS; ++i) { + locks[i] = 1; + } ready = false; for (i = 0; i < THREADS; ++i) { - stack = gc(malloc(FRAMESIZE)); - tid[i] = clone(Thrasher, stack + FRAMESIZE, - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD, - (void *)(intptr_t)i, ptid + i, tls + i, ctid + i); - EXPECT_NE(-1, tid[i]); + stack = gc(malloc(GetStackSize())); + tid[i] = clone(Thrasher, stack, GetStackSize(), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, + (void *)(intptr_t)i, 0, 0, 0, 0); + ASSERT_NE(-1, tid[i]); } ready = true; for (i = 0; i < THREADS; ++i) { - EXPECT_NE(-1, (rc = waitpid(0, &ws, 0))); - EXPECT_TRUE(WIFEXITED(ws)); - EXPECT_EQ(0, WEXITSTATUS(ws)); + _spinlock(locks + i); } - EXPECT_EQ(0, sigaction(SIGCHLD, &oldsa, 0)); - EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &oldss, 0)); + sigaction(SIGCHLD, &oldsa, 0); + sigprocmask(SIG_BLOCK, &oldss, 0); for (i = 0; i < ARRAYLEN(A); ++i) { EXPECT_NE(0, A[i], "i=%d", i); for (j = 0; j < ARRAYLEN(A); ++j) { diff --git a/test/libc/rand/test.mk b/test/libc/rand/test.mk index 9af26f7f5..a26a99a32 100644 --- a/test/libc/rand/test.mk +++ b/test/libc/rand/test.mk @@ -30,6 +30,7 @@ TEST_LIBC_RAND_DIRECTDEPS = \ LIBC_STR \ LIBC_STUBS \ LIBC_CALLS \ + LIBC_THREAD \ LIBC_LOG \ LIBC_SYSV \ LIBC_TESTLIB \ diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index b01c9e2c2..6675cd006 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -196,6 +196,7 @@ TEST(mmap, cowFileMapReadonlyFork) { } EXPECT_STREQN("hello", p, 5); EXPECT_NE(-1, munmap(p, 6)); + EXPECT_NE(-1, close(fd)); EXPECT_NE(-1, unlink(path)); } diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index a0dfc29a9..7e9961dcc 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -18,11 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/dirent.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/dt.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -31,6 +33,44 @@ STATIC_YOINK("usr/share/zoneinfo/New_York"); char testlib_enable_tmp_setup_teardown; +TEST(opendir, efault) { + ASSERT_SYS(EFAULT, NULL, opendir(0)); + if (!IsAsan()) return; // not possible + ASSERT_SYS(EFAULT, NULL, opendir((void *)77)); +} + +TEST(opendir, enoent) { + ASSERT_SYS(ENOENT, NULL, opendir("")); + ASSERT_SYS(ENOENT, NULL, opendir("o/foo")); +} + +TEST(opendir, enotdir) { + ASSERT_SYS(0, 0, close(creat("yo", 0644))); + ASSERT_SYS(ENOTDIR, NULL, opendir("yo/there")); +} + +TEST(dirstream, testDots) { + DIR *dir; + int hasdot = 0; + int hasdotdot = 0; + struct dirent *ent; + ASSERT_SYS(0, 0, close(creat("foo", 0644))); + ASSERT_NE(NULL, (dir = opendir("."))); + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".")) { + ++hasdot; + EXPECT_EQ(DT_DIR, ent->d_type); + } + if (!strcmp(ent->d_name, "..")) { + ++hasdotdot; + EXPECT_EQ(DT_DIR, ent->d_type); + } + } + EXPECT_EQ(1, hasdot); + EXPECT_EQ(1, hasdotdot); + EXPECT_SYS(0, 0, closedir(dir)); +} + TEST(dirstream, test) { DIR *dir; struct dirent *ent; @@ -45,8 +85,14 @@ TEST(dirstream, test) { EXPECT_NE(-1, touch(file2, 0644)); EXPECT_TRUE(NULL != (dir = opendir(dpath))); while ((ent = readdir(dir))) { - if (strcmp(ent->d_name, "foo")) hasfoo = true; - if (strcmp(ent->d_name, "bar")) hasbar = true; + if (!strcmp(ent->d_name, "foo")) { + EXPECT_EQ(DT_REG, ent->d_type); + hasfoo = true; + } + if (!strcmp(ent->d_name, "bar")) { + EXPECT_EQ(DT_REG, ent->d_type); + hasbar = true; + } } EXPECT_TRUE(hasfoo); EXPECT_TRUE(hasbar); diff --git a/test/libc/str/undeflate_test.c b/test/libc/str/undeflate_test.c index 93ddf2e50..2b2c0c9a1 100644 --- a/test/libc/str/undeflate_test.c +++ b/test/libc/str/undeflate_test.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" @@ -38,6 +39,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" #include "libc/zip.h" +#include "libc/zipos/zipos.internal.h" #include "third_party/zlib/zlib.h" STATIC_YOINK("zip_uri_support"); @@ -89,7 +91,7 @@ TEST(undeflate, testEmbeddedCompressedZipFile_theHardWay) { ASSERT_NE(-1, fstat(fd, &st)); ASSERT_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); - ASSERT_NE(NULL, (cd = zipfindcentraldir(map, st.st_size))); + ASSERT_NE(NULL, (cd = GetZipCdir(map, st.st_size))); ASSERT_GE(ZIP_CDIR_RECORDS(cd), 1); for (i = 0, cf = map + ZIP_CDIR_OFFSET(cd); i < ZIP_CDIR_RECORDS(cd); ++i, cf += ZIP_CFILE_HDRSIZE(cf)) { @@ -113,8 +115,6 @@ TEST(undeflate, testEmbeddedCompressedZipFile_theHardWay) { ASSERT_TRUE(found); } -#if 0 /* todo: don't rely on __zip_end */ - uint8_t *buf_; size_t bufsize_; uint8_t *data_; @@ -141,29 +141,20 @@ void Undeflate(void) { undeflate(buf_, uncompressedsize_, data_, compressedsize_, &ds_); } -static size_t GetLocalFile(const char *name) { - size_t i, cf, namesize; - namesize = strlen(name); - for (i = 0, cf = ZIP_CDIR_OFFSET(__zip_end); i < ZIP_CDIR_RECORDS(__zip_end); - ++i, cf += ZIP_CFILE_HDRSIZE(cf)) { - if (namesize == ZIP_CFILE_NAMESIZE(&_base[0] + cf) && - memcmp(name, ZIP_CFILE_NAME(&_base[0] + cf), namesize) == 0) { - return ZIP_CFILE_OFFSET(&_base[0] + cf); - } - } - abort(); -} - BENCH(undeflate, bench) { - size_t lf; - lf = GetLocalFile("libc/testlib/hyperion.txt"); - data_ = ZIP_LFILE_CONTENT(&_base[0] + lf); - compressedsize_ = ZIP_LFILE_COMPRESSEDSIZE(&_base[0] + lf); - uncompressedsize_ = ZIP_LFILE_UNCOMPRESSEDSIZE(&_base[0] + lf); + size_t cf, lf; + struct Zipos *zipos; + struct ZiposUri path; + zipos = __zipos_get(); + path.path = "libc/testlib/hyperion.txt"; + path.len = strlen(path.path); + cf = __zipos_find(zipos, &path); + lf = GetZipCfileOffset(zipos->map + cf); + data_ = ZIP_LFILE_CONTENT(zipos->map + lf); + compressedsize_ = ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf); + uncompressedsize_ = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf); bufsize_ = ROUNDUP(uncompressedsize_, FRAMESIZE / 2); buf_ = gc(malloc(bufsize_)); EZBENCH(donothing, Inflate()); EZBENCH(donothing, Undeflate()); } - -#endif diff --git a/test/libc/test.mk b/test/libc/test.mk index 7f7bbf676..02b30e514 100644 --- a/test/libc/test.mk +++ b/test/libc/test.mk @@ -18,6 +18,7 @@ o/$(MODE)/test/libc: \ o/$(MODE)/test/libc/sock \ o/$(MODE)/test/libc/stdio \ o/$(MODE)/test/libc/str \ + o/$(MODE)/test/libc/thread \ o/$(MODE)/test/libc/time \ o/$(MODE)/test/libc/tinymath \ o/$(MODE)/test/libc/unicode \ diff --git a/test/libc/runtime/clone_test.c b/test/libc/thread/clone_test.c similarity index 78% rename from test/libc/runtime/clone_test.c rename to test/libc/thread/clone_test.c index 3e3464965..e1d599b00 100644 --- a/test/libc/runtime/clone_test.c +++ b/test/libc/thread/clone_test.c @@ -16,35 +16,42 @@ │ 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/dce.h" -#include "libc/log/backtrace.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/gc.internal.h" -#include "libc/runtime/symbols.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/sysv/consts/clone.h" #include "libc/testlib/testlib.h" -#include "libc/time/time.h" -volatile int x; +int x, thechilde; +_Alignas(64) volatile char lock; + +void SetUp(void) { + x = 0; + lock = 0; + thechilde = 0; +} int thread(void *arg) { - return (x = 42); + x = 42; + ASSERT_EQ(23, (intptr_t)arg); + thechilde = gettid(); + _spunlock(&lock); + return 0; } TEST(clone, test) { - if (!IsLinux() && !IsNetbsd() && !IsWindows()) return; + if (IsXnu()) return; + if (IsOpenbsd()) return; // still flaky :'( + int me, tid; char *stack; - long double t; - int tid, ptid, ctid, tls; - t = nowl(); - stack = gc(malloc(FRAMESIZE)); - EXPECT_NE(-1, (tid = clone(thread, stack + FRAMESIZE, + me = gettid(); + _spinlock(&lock); + stack = _gc(valloc(STACKSIZE)); + ASSERT_NE(-1, (tid = clone(thread, stack, STACKSIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, - 0, &ptid, &tls, &ctid))); - while ((nowl() - t) < 1 && !x) { - __builtin_ia32_pause(); - } + (void *)23, 0, 0, 0, 0))); + _spinlock(&lock); ASSERT_EQ(42, x); + ASSERT_NE(me, tid); + ASSERT_EQ(tid, thechilde); } diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk new file mode 100644 index 000000000..33a46bc08 --- /dev/null +++ b/test/libc/thread/test.mk @@ -0,0 +1,71 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TEST_LIBC_THREAD + +TEST_LIBC_THREAD_SRCS := $(wildcard test/libc/thread/*.c) +TEST_LIBC_THREAD_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_THREAD_SRCS)) + +TEST_LIBC_THREAD_OBJS = \ + $(TEST_LIBC_THREAD_SRCS:%.c=o/$(MODE)/%.o) + +TEST_LIBC_THREAD_COMS = \ + $(TEST_LIBC_THREAD_SRCS:%.c=o/$(MODE)/%.com) + +TEST_LIBC_THREAD_BINS = \ + $(TEST_LIBC_THREAD_COMS) \ + $(TEST_LIBC_THREAD_COMS:%=%.dbg) + +TEST_LIBC_THREAD_TESTS = \ + $(TEST_LIBC_THREAD_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + +TEST_LIBC_THREAD_CHECKS = \ + $(TEST_LIBC_THREAD_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + +TEST_LIBC_THREAD_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ + LIBC_THREAD \ + LIBC_TESTLIB + +TEST_LIBC_THREAD_DEPS := \ + $(call uniq,$(foreach x,$(TEST_LIBC_THREAD_DIRECTDEPS),$($(x)))) + +o/$(MODE)/test/libc/thread/thread.pkg: \ + $(TEST_LIBC_THREAD_OBJS) \ + $(foreach x,$(TEST_LIBC_THREAD_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/test/libc/thread/%.com.dbg: \ + $(TEST_LIBC_THREAD_DEPS) \ + o/$(MODE)/test/libc/thread/%.o \ + o/$(MODE)/test/libc/thread/thread.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE) + @$(APELINK) + +$(TEST_LIBC_THREAD_OBJS): \ + DEFAULT_CCFLAGS += \ + -fno-builtin + +o/$(MODE)/test/libc/thread/getenv_test.com.runs: \ + o/$(MODE)/test/libc/thread/getenv_test.com + @HELLO=THERE build/runit $@ $< + +o/$(MODE)/test/libc/thread/fun_test.o \ +o/$(MODE)/test/libc/thread/itsatrap_test.o: \ + OVERRIDE_CFLAGS += \ + -fno-sanitize=all \ + -ftrapv + +.PHONY: o/$(MODE)/test/libc/thread +o/$(MODE)/test/libc/thread: \ + $(TEST_LIBC_THREAD_BINS) \ + $(TEST_LIBC_THREAD_CHECKS) diff --git a/test/libc/x/filecmp_test.c b/test/libc/x/filecmp_test.c index fd1c94f41..578d262d9 100644 --- a/test/libc/x/filecmp_test.c +++ b/test/libc/x/filecmp_test.c @@ -26,6 +26,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" +// TODO: This test flakes occasionally on Windows. +#if 0 + #define N (72 * 1024) char p[N]; @@ -59,3 +62,5 @@ BENCH(filecmp, bench) { EXPECT_EQ(0, xbarf("b", p, N)); EZBENCH2("filecmp", donothing, filecmp("a", "b")); } + +#endif diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index d91f65068..7bc87223b 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -126,6 +126,7 @@ TEST(redbean, testOptions) { "Content-Length: 0\r\n" "\r\n", gc(SendHttpRequest("OPTIONS * HTTP/1.1\n\n")))); + EXPECT_EQ(0, close(pipefds[0])); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); @@ -169,6 +170,7 @@ TEST(redbean, testPipeline) { "\r\n", gc(SendHttpRequest("OPTIONS * HTTP/1.1\n\n" "OPTIONS * HTTP/1.1\n\n")))); + EXPECT_EQ(0, close(pipefds[0])); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h index f36485f61..75eb581af 100644 --- a/third_party/chibicc/chibicc.h +++ b/third_party/chibicc/chibicc.h @@ -348,6 +348,8 @@ typedef enum { ND_LOAD, // Atomic load ND_TESTANDSET, // Atomic lock test and set ND_RELEASE, // Atomic lock release + ND_FETCHADD, // Atomic fetch and add + ND_SUBFETCH, // Atomic sub and fetch ND_FPCLASSIFY, // floating point classify ND_MOVNTDQ, // Intel MOVNTDQ ND_PMOVMSKB, // Intel PMOVMSKB diff --git a/third_party/chibicc/codegen.c b/third_party/chibicc/codegen.c index fb232f880..811ffd202 100644 --- a/third_party/chibicc/codegen.c +++ b/third_party/chibicc/codegen.c @@ -262,6 +262,20 @@ static char *reg_ax(int sz) { UNREACHABLE(); } +static char *reg_di(int sz) { + switch (sz) { + case 1: + return "%dil"; + case 2: + return "%di"; + case 4: + return "%edi"; + case 8: + return "%rdi"; + } + UNREACHABLE(); +} + static const char *gotpcrel(void) { if (opt_pic) { return "@gotpcrel(%rip)"; @@ -1542,6 +1556,26 @@ void gen_expr(Node *node) { println("\tmov\t%s,(%%rdi)", reg_ax(node->ty->size)); return; } + case ND_FETCHADD: { + gen_expr(node->lhs); + push(); + gen_expr(node->rhs); + pop("%rdi"); + println("\txadd\t%s,(%%rdi)", reg_ax(node->ty->size)); + return; + } + case ND_SUBFETCH: { + gen_expr(node->lhs); + push(); + gen_expr(node->rhs); + pop("%rdi"); + push(); + println("\tneg\t%s", reg_ax(node->ty->size)); + println("\txadd\t%s,(%%rdi)", reg_ax(node->ty->size)); + pop("%rdi"); + println("\tsub\t%s,%s", reg_di(node->ty->size), reg_ax(node->ty->size)); + return; + } case ND_RELEASE: { gen_expr(node->lhs); push(); diff --git a/third_party/chibicc/kw.gperf b/third_party/chibicc/kw.gperf index ab0b828d4..0f1c7777b 100644 --- a/third_party/chibicc/kw.gperf +++ b/third_party/chibicc/kw.gperf @@ -119,6 +119,8 @@ __builtin_types_compatible_p, KW___BUILTIN_TYPES_COMPATIBLE_P "->", KW_ARROW ".", KW_DOT __atomic_load, KW___ATOMIC_LOAD +__atomic_fetch_add, KW___ATOMIC_FETCH_ADD +__atomic_sub_fetch, KW___ATOMIC_SUB_FETCH __sync_lock_test_and_set, KW___SYNC_LOCK_TEST_AND_SET __sync_lock_release, KW___SYNC_LOCK_RELEASE __builtin_ia32_movntdq, KW___BUILTIN_IA32_MOVNTDQ diff --git a/third_party/chibicc/kw.h b/third_party/chibicc/kw.h index e5d1f8f13..62d8194eb 100644 --- a/third_party/chibicc/kw.h +++ b/third_party/chibicc/kw.h @@ -110,6 +110,8 @@ #define KW___SYNC_LOCK_RELEASE 126 #define KW___BUILTIN_IA32_PMOVMSKB128 127 #define KW___BUILTIN_IA32_MOVNTDQ 128 +#define KW___ATOMIC_FETCH_ADD 129 +#define KW___ATOMIC_SUB_FETCH 130 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/third_party/chibicc/kw.inc b/third_party/chibicc/kw.inc index c61680cb6..f570e2e53 100644 --- a/third_party/chibicc/kw.inc +++ b/third_party/chibicc/kw.inc @@ -37,7 +37,7 @@ #line 10 "kw.gperf" struct thatispacked KwSlot { char *name; unsigned char code; }; -#define TOTAL_KEYWORDS 114 +#define TOTAL_KEYWORDS 116 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 28 #define MIN_HASH_VALUE 1 @@ -230,7 +230,7 @@ LookupKw (register const char *str, register size_t len) {"||", KW_LOGOR}, #line 67 "kw.gperf" {"_Alignof", KW__ALIGNOF}, -#line 122 "kw.gperf" +#line 124 "kw.gperf" {"__sync_lock_test_and_set", KW___SYNC_LOCK_TEST_AND_SET}, #line 49 "kw.gperf" {"error", KW_ERROR}, @@ -249,7 +249,7 @@ LookupKw (register const char *str, register size_t len) {""}, #line 66 "kw.gperf" {"_Alignas", KW__ALIGNAS}, -#line 123 "kw.gperf" +#line 125 "kw.gperf" {"__sync_lock_release", KW___SYNC_LOCK_RELEASE}, {""}, #line 39 "kw.gperf" @@ -340,20 +340,22 @@ LookupKw (register const char *str, register size_t len) {""}, {""}, {""}, #line 100 "kw.gperf" {"__builtin_strlen", KW___BUILTIN_STRLEN}, -#line 124 "kw.gperf" +#line 126 "kw.gperf" {"__builtin_ia32_movntdq", KW___BUILTIN_IA32_MOVNTDQ}, {""}, {""}, {""}, #line 120 "kw.gperf" {".", KW_DOT}, #line 36 "kw.gperf" {"include", KW_INCLUDE}, - {""}, {""}, {""}, +#line 122 "kw.gperf" + {"__atomic_fetch_add", KW___ATOMIC_FETCH_ADD}, + {""}, {""}, #line 14 "kw.gperf" {"return", KW_RETURN}, {""}, {""}, {""}, #line 26 "kw.gperf" {"union", KW_UNION}, -#line 125 "kw.gperf" +#line 127 "kw.gperf" {"__builtin_ia32_pmovmskb128", KW___BUILTIN_IA32_PMOVMSKB128}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 112 "kw.gperf" @@ -373,7 +375,10 @@ LookupKw (register const char *str, register size_t len) {""}, {""}, {""}, {""}, #line 53 "kw.gperf" {"inline", KW_INLINE}, - {""}, {""}, {""}, {""}, + {""}, +#line 123 "kw.gperf" + {"__atomic_sub_fetch", KW___ATOMIC_SUB_FETCH}, + {""}, {""}, #line 113 "kw.gperf" {"!", KW_EXCLAIM} }; diff --git a/third_party/chibicc/parse.c b/third_party/chibicc/parse.c index fe2b5b8cc..ba44c9510 100644 --- a/third_party/chibicc/parse.c +++ b/third_party/chibicc/parse.c @@ -3243,6 +3243,32 @@ static Node *primary(Token **rest, Token *tok) { *rest = skip(tok, ')'); return node; } + if (kw == KW___ATOMIC_FETCH_ADD) { + Node *node = new_node(ND_FETCHADD, tok); + tok = skip(tok->next, '('); + node->lhs = assign(&tok, tok); + add_type(node->lhs); + node->ty = node->lhs->ty->base; + tok = skip(tok, ','); + node->rhs = assign(&tok, tok); + tok = skip(tok, ','); + const_expr(&tok, tok); + *rest = skip(tok, ')'); + return node; + } + if (kw == KW___ATOMIC_SUB_FETCH) { + Node *node = new_node(ND_SUBFETCH, tok); + tok = skip(tok->next, '('); + node->lhs = assign(&tok, tok); + add_type(node->lhs); + node->ty = node->lhs->ty->base; + tok = skip(tok, ','); + node->rhs = assign(&tok, tok); + tok = skip(tok, ','); + const_expr(&tok, tok); + *rest = skip(tok, ')'); + return node; + } if (kw == KW___SYNC_LOCK_TEST_AND_SET) { Node *node = new_node(ND_TESTANDSET, tok); tok = skip(tok->next, '('); diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 7aac8ab61..9ac615efd 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -50,6 +50,10 @@ │ ALT-B BACKWARD WORD │ │ CTRL-ALT-F FORWARD EXPR │ │ CTRL-ALT-B BACKWARD EXPR │ +│ ALT-RIGHT FORWARD EXPR │ +│ ALT-LEFT BACKWARD EXPR │ +│ ALT-SHIFT-B BARF EXPR │ +│ ALT-SHIFT-S SLURP EXPR │ │ CTRL-K KILL LINE FORWARDS │ │ CTRL-U KILL LINE BACKWARDS │ │ ALT-H KILL WORD BACKWARDS │ @@ -58,12 +62,13 @@ │ ALT-D KILL WORD FORWARDS │ │ CTRL-Y YANK │ │ ALT-Y ROTATE KILL RING AND YANK AGAIN │ +│ ALT-\ SQUEEZE ADJACENT WHITESPACE │ │ CTRL-T TRANSPOSE │ │ ALT-T TRANSPOSE WORD │ │ ALT-U UPPERCASE WORD │ │ ALT-L LOWERCASE WORD │ │ ALT-C CAPITALIZE WORD │ -│ CTRL-C INTERRUPT PROCESS │ +│ CTRL-C CTRL-C INTERRUPT PROCESS │ │ CTRL-Z SUSPEND PROCESS │ │ CTRL-\ QUIT PROCESS │ │ CTRL-S PAUSE OUTPUT │ @@ -71,6 +76,7 @@ │ CTRL-Q ESCAPED INSERT │ │ CTRL-SPACE SET MARK │ │ CTRL-X CTRL-X GOTO MARK │ +│ PROTIP REMAP CAPS LOCK TO CTRL │ │ │ │ EXAMPLE │ │ │ @@ -156,6 +162,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" #include "libc/unicode/unicode.h" +#include "net/http/escape.h" #include "third_party/linenoise/linenoise.h" #include "tool/build/lib/case.h" @@ -305,6 +312,14 @@ static int notwseparator(wint_t c) { return !iswseparator(c); } +static int iswname(wint_t c) { + return !iswseparator(c) || c == '_' || c == '-' || c == '.'; +} + +static int notwname(wint_t c) { + return !iswname(c); +} + static void linenoiseOnInt(int sig) { gotint = sig; } @@ -817,34 +832,116 @@ static char linenoiseGrow(struct linenoiseState *ls, size_t n) { return 1; } -static size_t linenoiseMaxCompletionLength(linenoiseCompletions *lc) { +static wint_t ScrubCompletionCharacter(wint_t c) { + if ((0x00 <= c && c <= 0x1F) || c == 0x7F) { + return kCp437[c]; + } else { + return c; + } +} + +static size_t linenoiseMaxCompletionWidth(linenoiseCompletions *lc) { size_t i, n, m; for (m = i = 0; i < lc->len; ++i) { - n = strlen(lc->cvec[i]); + n = GetMonospaceWidth(lc->cvec[i], strlen(lc->cvec[i]), 0); m = MAX(n, m); } return m; } +static size_t Forward(struct linenoiseState *l, size_t pos) { + return pos + GetUtf8(l->buf + pos, l->len - pos).n; +} + +static size_t Backward(struct linenoiseState *l, size_t pos) { + if (pos) { + do --pos; + while (pos && (l->buf[pos] & 0300) == 0200); + } + return pos; +} + +static size_t Backwards(struct linenoiseState *l, size_t pos, + int pred(wint_t)) { + size_t i; + struct rune r; + while (pos) { + i = Backward(l, pos); + r = GetUtf8(l->buf + i, l->len - i); + if (pred(r.c)) { + pos = i; + } else { + break; + } + } + return pos; +} + +static size_t Forwards(struct linenoiseState *l, size_t pos, int pred(wint_t)) { + struct rune r; + while (pos < l->len) { + r = GetUtf8(l->buf + pos, l->len - pos); + if (pred(r.c)) { + pos += r.n; + } else { + break; + } + } + return pos; +} + +static size_t GetCommonPrefixLength(struct linenoiseCompletions *lc) { + struct rune r; + int i, j, n, c; + i = 0; + for (n = -1, i = 0; i < lc->len; ++i) { + if (n != -1) { + n = strnlen(lc->cvec[i], n); + } else { + n = strlen(lc->cvec[i]); + } + } + for (i = 0, r.n = 0; i < n; i += r.n) { + for (c = -1, j = 0; j < lc->len; ++j) { + r = GetUtf8(lc->cvec[j] + i, n - i); + if (c != -1) { + if (r.c != c) { + goto Finished; + } + } else { + c = r.c; + } + } + } +Finished: + return i; +} + /** * Performs tab completion similar in behavior to bash and readline. */ static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq, int size) { + char *s; ssize_t nread; struct abuf ab; linenoiseCompletions lc; struct linenoiseState saved; - size_t i, j, k, n, perline, itemlen; + size_t i, j, k, n, m, perline, itemlen; // we know that the user pressed tab once nread = 0; bzero(&lc, sizeof(lc)); - completionCallback(ls->buf, &lc); - if (lc.len == 1) { - // if there's a single completion, complete it, and return - n = strlen(lc.cvec[0]); + i = Backwards(ls, ls->pos, iswname); + j = Forwards(ls, ls->pos, iswname); + s = strndup(ls->buf + i, j - i); + completionCallback(s, &lc); + m = GetCommonPrefixLength(&lc); + if (m > j - i) { + // on common prefix (or single completion) we complete and return + n = i + m + (ls->len - j); if (linenoiseGrow(ls, n + 1)) { - memcpy(ls->buf, lc.cvec[0], n + 1); + memmove(ls->buf + i + m, ls->buf + i + j, ls->len - j + 1); + memcpy(ls->buf + i, lc.cvec[0], m); ls->len = ls->pos = n; } linenoiseRefreshLine(ls); @@ -853,10 +950,15 @@ static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq, // if there's a multiline completions, then do nothing and wait and // see if the user presses tab again. if the user does this we then // print ALL the completions, to above the editing line + for (i = 0; i < lc.len; ++i) { + s = lc.cvec[i]; + lc.cvec[i] = VisualizeControlCodes(s, -1, 0); + free(s); + } for (;;) { nread = linenoiseRead(ls->ifd, seq, size, ls); if (nread == 1 && seq[0] == '\t') { - itemlen = linenoiseMaxCompletionLength(&lc) + 4; + itemlen = linenoiseMaxCompletionWidth(&lc) + 4; perline = MAX(1, (ls->ws.ws_col - 1) / itemlen); abInit(&ab); abAppends(&ab, "\r\n\033[K"); @@ -1039,18 +1141,6 @@ static char *linenoiseRefreshHints(struct linenoiseState *l) { return ab.b; } -static size_t Forward(struct linenoiseState *l, size_t pos) { - return pos + GetUtf8(l->buf + pos, l->len - pos).n; -} - -static size_t Backward(struct linenoiseState *l, size_t pos) { - if (pos) { - do --pos; - while (pos && (l->buf[pos] & 0300) == 0200); - } - return pos; -} - static int linenoiseMirrorLeft(struct linenoiseState *l, unsigned res[2]) { unsigned c, pos, left, right, depth, index; if ((pos = Backward(l, l->pos))) { @@ -1350,35 +1440,6 @@ static void linenoiseEditRefresh(struct linenoiseState *l) { linenoiseRefreshLine(l); } -static size_t Backwards(struct linenoiseState *l, size_t pos, - int pred(wint_t)) { - size_t i; - struct rune r; - while (pos) { - i = Backward(l, pos); - r = GetUtf8(l->buf + i, l->len - i); - if (pred(r.c)) { - pos = i; - } else { - break; - } - } - return pos; -} - -static size_t Forwards(struct linenoiseState *l, size_t pos, int pred(wint_t)) { - struct rune r; - while (pos < l->len) { - r = GetUtf8(l->buf + pos, l->len - pos); - if (pred(r.c)) { - pos += r.n; - } else { - break; - } - } - return pos; -} - static size_t ForwardWord(struct linenoiseState *l, size_t pos) { pos = Forwards(l, pos, iswseparator); pos = Forwards(l, pos, notwseparator); @@ -1711,6 +1772,145 @@ static void linenoiseEditCtrlq(struct linenoiseState *l) { } } +/** + * Moves last item inside current s-expression to outside, e.g. + * + * (a| b c) + * (a| b) c + * + * The cursor position changes only if a paren is moved before it: + * + * (a b c |) + * (a b) c | + * + * To accommodate non-LISP languages we connect unspaced outer symbols: + * + * f(a,| b, g()) + * f(a,| b), g() + * + * Our standard keybinding is ALT-SHIFT-B. + */ +static void linenoiseEditBarf(struct linenoiseState *l) { + struct rune r; + unsigned long w; + size_t i, pos, depth = 0; + unsigned lhs, rhs, end, *stack = 0; + /* go as far right within current s-expr as possible */ + for (pos = l->pos;; pos += r.n) { + if (pos == l->len) goto Finish; + r = GetUtf8(l->buf + pos, l->len - pos); + if (depth) { + if (r.c == stack[depth - 1]) { + --depth; + } + } else { + if ((rhs = GetMirrorRight(r.c))) { + stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack)); + stack[depth - 1] = rhs; + } else if (GetMirrorLeft(r.c)) { + end = pos; + break; + } + } + } + /* go back one item */ + pos = Backwards(l, pos, isxseparator); + for (;; pos = i) { + if (!pos) goto Finish; + i = Backward(l, pos); + r = GetUtf8(l->buf + i, l->len - i); + if (depth) { + if (r.c == stack[depth - 1]) { + --depth; + } + } else { + if ((lhs = GetMirrorLeft(r.c))) { + stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack)); + stack[depth - 1] = lhs; + } else if (iswseparator(r.c)) { + break; + } + } + } + pos = Backwards(l, pos, isxseparator); + /* now move the text */ + r = GetUtf8(l->buf + end, l->len - end); + memmove(l->buf + pos + r.n, l->buf + pos, end - pos); + w = tpenc(r.c); + for (i = 0; i < r.n; ++i) { + l->buf[pos + i] = w; + w >>= 8; + } + if (l->pos > pos) { + l->pos += r.n; + } + linenoiseRefreshLine(l); +Finish: + free(stack); +} + +/** + * Moves first item outside current s-expression to inside, e.g. + * + * (a| b) c d + * (a| b c) d + * + * To accommodate non-LISP languages we connect unspaced outer symbols: + * + * f(a,| b), g() + * f(a,| b, g()) + * + * Our standard keybinding is ALT-SHIFT-S. + */ +static void linenoiseEditSlurp(struct linenoiseState *l) { + char rp[6]; + struct rune r; + size_t pos, depth = 0; + unsigned rhs, point = 0, start = 0, *stack = 0; + /* go to outside edge of current s-expr */ + for (pos = l->pos; pos < l->len; pos += r.n) { + r = GetUtf8(l->buf + pos, l->len - pos); + if (depth) { + if (r.c == stack[depth - 1]) { + --depth; + } + } else { + if ((rhs = GetMirrorRight(r.c))) { + stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack)); + stack[depth - 1] = rhs; + } else if (GetMirrorLeft(r.c)) { + point = pos; + pos += r.n; + start = pos; + break; + } + } + } + /* go forward one item */ + pos = Forwards(l, pos, isxseparator); + for (; pos < l->len; pos += r.n) { + r = GetUtf8(l->buf + pos, l->len - pos); + if (depth) { + if (r.c == stack[depth - 1]) { + --depth; + } + } else { + if ((rhs = GetMirrorRight(r.c))) { + stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack)); + stack[depth - 1] = rhs; + } else if (iswseparator(r.c)) { + break; + } + } + } + /* now move the text */ + memcpy(rp, l->buf + point, start - point); + memmove(l->buf + point, l->buf + start, pos - start); + memcpy(l->buf + pos - (start - point), rp, start - point); + linenoiseRefreshLine(l); + free(stack); +} + /** * Runs linenoise engine. * @@ -1782,10 +1982,19 @@ static ssize_t linenoiseEdit(int stdin_fd, int stdout_fd, const char *prompt, CASE(CTRL('L'), linenoiseEditRefresh(&l)); CASE(CTRL('Z'), linenoiseEditSuspend(&l)); CASE(CTRL('U'), linenoiseEditKillLeft(&l)); - CASE(CTRL('C'), linenoiseEditInterrupt(&l)); CASE(CTRL('T'), linenoiseEditTranspose(&l)); CASE(CTRL('K'), linenoiseEditKillRight(&l)); CASE(CTRL('W'), linenoiseEditRuboutWord(&l)); + case CTRL('C'): + if (linenoiseRead(l.ifd, seq, sizeof(seq), &l) != 1) break; + switch (seq[0]) { + CASE(CTRL('C'), linenoiseEditInterrupt(&l)); + CASE(CTRL('B'), linenoiseEditBarf(&l)); + CASE(CTRL('S'), linenoiseEditSlurp(&l)); + default: + break; + } + break; case CTRL('X'): if (l.seq[1][0] == CTRL('X')) { linenoiseEditGoto(&l); diff --git a/third_party/linenoise/linenoise.mk b/third_party/linenoise/linenoise.mk index ffd074d4f..b19eb14d4 100644 --- a/third_party/linenoise/linenoise.mk +++ b/third_party/linenoise/linenoise.mk @@ -28,7 +28,8 @@ THIRD_PARTY_LINENOISE_A_DIRECTDEPS = \ LIBC_SYSV_CALLS \ LIBC_STR \ LIBC_UNICODE \ - LIBC_STUBS + LIBC_STUBS \ + NET_HTTP THIRD_PARTY_LINENOISE_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_LINENOISE_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/python/Modules/_pickle.c b/third_party/python/Modules/_pickle.c index 34eff3a1e..0d5df16c8 100644 --- a/third_party/python/Modules/_pickle.c +++ b/third_party/python/Modules/_pickle.c @@ -653,7 +653,7 @@ typedef struct PicklerObject { is an unbound method, NULL otherwise */ PyObject *dispatch_table; /* private dispatch_table, can be NULL */ - PyObject *write; /* write() method of the output stream. */ + PyObject *write_; /* write() method of the output stream. */ PyObject *output_buffer; /* Write into a local bytearray buffer before flushing to the stream. */ Py_ssize_t output_len; /* Length of output_buffer. */ @@ -699,7 +699,7 @@ typedef struct UnpicklerObject { Py_ssize_t next_read_idx; Py_ssize_t prefetched_idx; /* index of first prefetched byte */ - PyObject *read; /* read() method of the input stream. */ + PyObject *read_; /* read() method of the input stream. */ PyObject *readline; /* readline() method of the input stream. */ PyObject *peek; /* peek() method of the input stream, or NULL */ @@ -1045,14 +1045,14 @@ _Pickler_FlushToFile(PicklerObject *self) { PyObject *output, *result; - assert(self->write != NULL); + assert(self->write_ != NULL); /* This will commit the frame first */ output = _Pickler_GetString(self); if (output == NULL) return -1; - result = _Pickle_FastCall(self->write, output); + result = _Pickle_FastCall(self->write_, output); Py_XDECREF(result); return (result == NULL) ? -1 : 0; } @@ -1118,7 +1118,7 @@ _Pickler_New(void) self->pers_func = NULL; self->dispatch_table = NULL; - self->write = NULL; + self->write_ = NULL; self->proto = 0; self->bin = 0; self->framing = 0; @@ -1175,8 +1175,8 @@ _Pickler_SetOutputStream(PicklerObject *self, PyObject *file) { _Py_IDENTIFIER(write); assert(file != NULL); - self->write = _PyObject_GetAttrId(file, &PyId_write); - if (self->write == NULL) { + self->write_ = _PyObject_GetAttrId(file, &PyId_write); + if (self->write_ == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetString(PyExc_TypeError, "file must have a 'write' attribute"); @@ -1222,7 +1222,7 @@ _Unpickler_SkipConsumed(UnpicklerObject *self) assert(self->peek); /* otherwise we did something wrong */ /* This makes a useless copy... */ - r = PyObject_CallFunction(self->read, "n", consumed); + r = PyObject_CallFunction(self->read_, "n", consumed); if (r == NULL) return -1; Py_DECREF(r); @@ -1253,7 +1253,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) PyObject *data; Py_ssize_t read_size; - assert(self->read != NULL); + assert(self->read_ != NULL); if (_Unpickler_SkipConsumed(self) < 0) return -1; @@ -1287,7 +1287,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) len = PyLong_FromSsize_t(n); if (len == NULL) return -1; - data = _Pickle_FastCall(self->read, len); + data = _Pickle_FastCall(self->read_, len); } if (data == NULL) return -1; @@ -1314,7 +1314,7 @@ _Unpickler_ReadImpl(UnpicklerObject *self, char **s, Py_ssize_t n) /* This case is handled by the _Unpickler_Read() macro for efficiency */ assert(self->next_read_idx + n > self->input_len); - if (!self->read) + if (!self->read_) return bad_readline(); num_read = _Unpickler_ReadFromFile(self, n); @@ -1381,7 +1381,7 @@ _Unpickler_Readline(UnpicklerObject *self, char **result) return _Unpickler_CopyLine(self, line_start, num_read, result); } } - if (!self->read) + if (!self->read_) return bad_readline(); num_read = _Unpickler_ReadFromFile(self, READ_WHOLE_LINE); @@ -1493,7 +1493,7 @@ _Unpickler_New(void) self->input_len = 0; self->next_read_idx = 0; self->prefetched_idx = 0; - self->read = NULL; + self->read_ = NULL; self->readline = NULL; self->peek = NULL; self->encoding = NULL; @@ -1533,13 +1533,13 @@ _Unpickler_SetInputStream(UnpicklerObject *self, PyObject *file) else return -1; } - self->read = _PyObject_GetAttrId(file, &PyId_read); + self->read_ = _PyObject_GetAttrId(file, &PyId_read); self->readline = _PyObject_GetAttrId(file, &PyId_readline); - if (self->readline == NULL || self->read == NULL) { + if (self->readline == NULL || self->read_ == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetString(PyExc_TypeError, "file must have 'read' and 'readline' attributes"); - Py_CLEAR(self->read); + Py_CLEAR(self->read_); Py_CLEAR(self->readline); Py_CLEAR(self->peek); return -1; @@ -4158,7 +4158,7 @@ _pickle_Pickler_dump(PicklerObject *self, PyObject *obj) /* Check whether the Pickler was initialized correctly (issue3664). Developers often forget to call __init__() in their subclasses, which would trigger a segfault without this check. */ - if (self->write == NULL) { + if (self->write_ == NULL) { PickleState *st = _Pickle_GetGlobalState(); PyErr_Format(st->PicklingError, "Pickler.__init__() was not called by %s.__init__()", @@ -4218,7 +4218,7 @@ Pickler_dealloc(PicklerObject *self) PyObject_GC_UnTrack(self); Py_XDECREF(self->output_buffer); - Py_XDECREF(self->write); + Py_XDECREF(self->write_); Py_XDECREF(self->pers_func); Py_XDECREF(self->dispatch_table); Py_XDECREF(self->fast_memo); @@ -4231,7 +4231,7 @@ Pickler_dealloc(PicklerObject *self) static int Pickler_traverse(PicklerObject *self, visitproc visit, void *arg) { - Py_VISIT(self->write); + Py_VISIT(self->write_); Py_VISIT(self->pers_func); Py_VISIT(self->dispatch_table); Py_VISIT(self->fast_memo); @@ -4242,7 +4242,7 @@ static int Pickler_clear(PicklerObject *self) { Py_CLEAR(self->output_buffer); - Py_CLEAR(self->write); + Py_CLEAR(self->write_); Py_CLEAR(self->pers_func); Py_CLEAR(self->dispatch_table); Py_CLEAR(self->fast_memo); @@ -4293,7 +4293,7 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, _Py_IDENTIFIER(dispatch_table); /* In case of multiple __init__() calls, clear previous content. */ - if (self->write != NULL) + if (self->write_ != NULL) (void)Pickler_clear(self); if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0) @@ -6514,9 +6514,9 @@ _pickle_Unpickler_load_impl(UnpicklerObject *self) /* Check whether the Unpickler was initialized correctly. This prevents segfaulting if a subclass overridden __init__ with a function that does - not call Unpickler.__init__(). Here, we simply ensure that self->read + not call Unpickler.__init__(). Here, we simply ensure that self->read_ is not NULL. */ - if (unpickler->read == NULL) { + if (unpickler->read_ == NULL) { PickleState *st = _Pickle_GetGlobalState(); PyErr_Format(st->UnpicklingError, "Unpickler.__init__() was not called by %s.__init__()", @@ -6676,7 +6676,7 @@ Unpickler_dealloc(UnpicklerObject *self) { PyObject_GC_UnTrack((PyObject *)self); Py_XDECREF(self->readline); - Py_XDECREF(self->read); + Py_XDECREF(self->read_); Py_XDECREF(self->peek); Py_XDECREF(self->stack); Py_XDECREF(self->pers_func); @@ -6698,7 +6698,7 @@ static int Unpickler_traverse(UnpicklerObject *self, visitproc visit, void *arg) { Py_VISIT(self->readline); - Py_VISIT(self->read); + Py_VISIT(self->read_); Py_VISIT(self->peek); Py_VISIT(self->stack); Py_VISIT(self->pers_func); @@ -6709,7 +6709,7 @@ static int Unpickler_clear(UnpicklerObject *self) { Py_CLEAR(self->readline); - Py_CLEAR(self->read); + Py_CLEAR(self->read_); Py_CLEAR(self->peek); Py_CLEAR(self->stack); Py_CLEAR(self->pers_func); @@ -6772,7 +6772,7 @@ _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, _Py_IDENTIFIER(persistent_load); /* In case of multiple __init__() calls, clear previous content. */ - if (self->read != NULL) + if (self->read_ != NULL) (void)Unpickler_clear(self); if (_Unpickler_SetInputStream(self, file) < 0) diff --git a/third_party/python/Modules/expat/xmlparse.c b/third_party/python/Modules/expat/xmlparse.c index 831d46e99..d19b906ac 100644 --- a/third_party/python/Modules/expat/xmlparse.c +++ b/third_party/python/Modules/expat/xmlparse.c @@ -219,7 +219,7 @@ typedef struct { const XML_Char *base; const XML_Char *publicId; const XML_Char *notation; - XML_Bool open; + XML_Bool open_; XML_Bool is_param; XML_Bool is_internal; /* true if declared in internal subset outside PE */ } ENTITY; @@ -2276,7 +2276,7 @@ static enum XML_Error doContent(XML_Parser parser, int startTagLevel, reportDefault(parser, enc, s, next); break; } - if (entity->open) return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->open_) return XML_ERROR_RECURSIVE_ENTITY_REF; if (entity->notation) return XML_ERROR_BINARY_ENTITY_REF; if (entity->textPtr) { enum XML_Error result; @@ -2292,9 +2292,9 @@ static enum XML_Error doContent(XML_Parser parser, int startTagLevel, if (result != XML_ERROR_NONE) return result; } else if (parser->m_externalEntityRefHandler) { const XML_Char *context; - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; context = getContext(parser); - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; if (!context) return XML_ERROR_NO_MEMORY; if (!parser->m_externalEntityRefHandler( parser->m_externalEntityRefHandlerArg, context, entity->base, @@ -4417,7 +4417,7 @@ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, } break; } - if (entity->open) return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->open_) return XML_ERROR_RECURSIVE_ENTITY_REF; if (entity->textPtr) { enum XML_Error result; XML_Bool betweenDecl = @@ -4429,14 +4429,14 @@ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, } if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; if (!parser->m_externalEntityRefHandler( parser->m_externalEntityRefHandlerArg, 0, entity->base, entity->systemId, entity->publicId)) { - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; return XML_ERROR_EXTERNAL_ENTITY_HANDLING; } - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; handleDefault = XML_FALSE; if (!dtd->paramEntityRead) { dtd->keepProcessing = dtd->standalone; @@ -4692,7 +4692,7 @@ static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity, (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); if (!openEntity) return XML_ERROR_NO_MEMORY; } - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; entity->processed = 0; openEntity->next = parser->m_openInternalEntities; parser->m_openInternalEntities = openEntity; @@ -4722,7 +4722,7 @@ static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity, entity->processed = (int)(next - textStart); parser->m_processor = internalEntityProcessor; } else { - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; parser->m_openInternalEntities = openEntity->next; /* put openEntity back in list of free instances */ openEntity->next = parser->m_freeInternalEntities; @@ -4768,7 +4768,7 @@ static enum XML_Error internalEntityProcessor(XML_Parser parser, entity->processed = (int)(next - (char *)entity->textPtr); return result; } else { - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; parser->m_openInternalEntities = openEntity->next; /* put openEntity back in list of free instances */ openEntity->next = parser->m_freeInternalEntities; @@ -4915,11 +4915,11 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, */ break; } - if (entity->open) { + if (entity->open_) { if (enc == parser->m_encoding) { /* It does not appear that this line can be executed. * - * The "if (entity->open)" check catches recursive entity + * The "if (entity->open_)" check catches recursive entity * definitions. In order to be called with an open * entity, it must have gone through this code before and * been through the recursive call to @@ -4928,7 +4928,7 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, * internal encoding (internal_utf8 or internal_utf16), * which can never be the same as the principle encoding. * It doesn't appear there is another code path that gets - * here with entity->open being TRUE. + * here with entity->open_ being TRUE. * * Since it is not certain that this logic is watertight, * we keep the line and merely exclude it from coverage @@ -4948,11 +4948,11 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, } else { enum XML_Error result; const XML_Char *textEnd = entity->textPtr + entity->textLen; - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata, (char *)entity->textPtr, (char *)textEnd, pool); - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; if (result) return result; } } break; @@ -5022,7 +5022,7 @@ static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, dtd->keepProcessing = dtd->standalone; goto endEntityValue; } - if (entity->open) { + if (entity->open_) { if (enc == parser->m_encoding) parser->m_eventPtr = entityTextPtr; result = XML_ERROR_RECURSIVE_ENTITY_REF; goto endEntityValue; @@ -5030,24 +5030,24 @@ static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, if (entity->systemId) { if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; if (!parser->m_externalEntityRefHandler( parser->m_externalEntityRefHandlerArg, 0, entity->base, entity->systemId, entity->publicId)) { - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; goto endEntityValue; } - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; if (!dtd->paramEntityRead) dtd->keepProcessing = dtd->standalone; } else dtd->keepProcessing = dtd->standalone; } else { - entity->open = XML_TRUE; + entity->open_ = XML_TRUE; result = storeEntityValue( parser, parser->m_internalEncoding, (char *)entity->textPtr, (char *)(entity->textPtr + entity->textLen)); - entity->open = XML_FALSE; + entity->open_ = XML_FALSE; if (result) goto endEntityValue; } break; @@ -5431,7 +5431,7 @@ static const XML_Char *getContext(XML_Parser parser) { const XML_Char *s; ENTITY *e = (ENTITY *)hashTableIterNext(&iter); if (!e) break; - if (!e->open) continue; + if (!e->open_) continue; if (needSep && !poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) return NULL; for (s = e->name; *s; s++) @@ -5453,7 +5453,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context) { if (!poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&parser->m_tempPool), 0); - if (e) e->open = XML_TRUE; + if (e) e->open_ = XML_TRUE; if (*s != XML_T('\0')) s++; context = s; poolDiscard(&parser->m_tempPool); diff --git a/third_party/python/python.mk b/third_party/python/python.mk index feeecc758..a08da0d6d 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -4113,28 +4113,28 @@ $(THIRD_PARTY_PYTHON_STAGE2_A_DATA_OBJS): ZIPOBJ_FLAGS += -P.python -C3 $(THIRD_PARTY_PYTHON_PYTEST_A_PYS_OBJS): PYFLAGS += -P.python -C3 $(THIRD_PARTY_PYTHON_PYTEST_A_DATA_OBJS): ZIPOBJ_FLAGS += -P.python -C3 -o/$(MODE)/third_party/python/Python/ceval.o: QUOTA = -C32 -M1024m -o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C32 -M1024m +o/$(MODE)/third_party/python/Python/ceval.o: QUOTA = -C64 -M1024m +o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C64 -M1024m o/$(MODE)/third_party/python/Parser/asdl_c.o: PYFLAGS += -m $(THIRD_PARTY_PYTHON_PYTEST_PYMAINS_OBJS): PYFLAGS += -t -P.python -C3 $(THIRD_PARTY_PYTHON_PYTEST_TODOS:%.py=o/$(MODE)/%.o): PYFLAGS += -t -P.python -C3 o/$(MODE)/third_party/python/Lib/test/pystone.o: PYFLAGS += -m -O2 -P.python -C4 -o/$(MODE)/third_party/python/Lib/test/test_long.py.runs: QUOTA = -C32 -o/$(MODE)/third_party/python/Lib/test/test_hash.py.runs: QUOTA = -C32 -o/$(MODE)/third_party/python/Lib/test/test_exceptions.py.runs: QUOTA = -C32 +o/$(MODE)/third_party/python/Lib/test/test_long.py.runs: QUOTA = -C64 +o/$(MODE)/third_party/python/Lib/test/test_hash.py.runs: QUOTA = -C64 +o/$(MODE)/third_party/python/Lib/test/test_exceptions.py.runs: QUOTA = -C64 o/$(MODE)/third_party/python/Lib/test/test_tuple.py.runs: QUOTA = -M512m o/$(MODE)/third_party/python/Lib/test/test_decimal.py.runs: QUOTA = -M512m -C64 o/$(MODE)/third_party/python/Lib/test/test_longexp.py.runs: QUOTA = -M1024m o/$(MODE)/third_party/python/Lib/test/test_unicode.py.runs: QUOTA = -M1400m -o/$(MODE)/third_party/python/Lib/test/test_unicodedata.py.runs: QUOTA = -C32 +o/$(MODE)/third_party/python/Lib/test/test_unicodedata.py.runs: QUOTA = -C64 o/$(MODE)/third_party/python/Lib/test/test_logging.py.runs: QUOTA = -M512m o/$(MODE)/third_party/python/Lib/test/test_itertools.py.runs: QUOTA = -M1024m o/$(MODE)/third_party/python/Lib/test/test_tarfile.py.runs: QUOTA = -L120 -C64 o/$(MODE)/third_party/python/Lib/test/test_sqlite.py.runs: QUOTA = -L120 o/$(MODE)/third_party/python/Lib/test/test_gzip.py.runs: QUOTA = -L120 -o/$(MODE)/third_party/python/Lib/test/test_email/test_email.py.runs: QUOTA = -C16 -M1024m +o/$(MODE)/third_party/python/Lib/test/test_email/test_email.py.runs: QUOTA = -C32 -M1024m THIRD_PARTY_PYTHON_LIBS = \ $(foreach x,$(THIRD_PARTY_PYTHON_ARTIFACTS),$($(x))) diff --git a/tool/build/compile.c b/tool/build/compile.c index a30df1e30..59b94ee73 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -40,6 +40,7 @@ #include "libc/math.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/kcpuids.h" +#include "libc/nexgen32e/x86feature.h" #include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" #include "libc/stdio/append.internal.h" @@ -841,6 +842,12 @@ int main(int argc, char *argv[]) { if (isclang && IsGccOnlyFlag(argv[i])) { continue; } + if (!X86_HAVE(AVX) && + (!strcmp(argv[i], "-msse2avx") || !strcmp(argv[i], "-Wa,-msse2avx"))) { + // Avoid any chance of people using Intel's older or low power + // CPUs encountering a SIGILL error due to these awesome flags + continue; + } if (!strcmp(argv[i], "-w")) { AddArg(argv[i]); AddArg("-D__W__"); diff --git a/tool/build/runit.c b/tool/build/runit.c index 957ed7749..b6a31568e 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -55,7 +55,7 @@ #include "tool/build/lib/psk.h" #include "tool/build/runit.h" -#define MAX_WAIT_CONNECT_SECONDS 30 +#define MAX_WAIT_CONNECT_SECONDS 12 #define INITIAL_CONNECT_TIMEOUT 100000 /** @@ -170,81 +170,70 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) { char *args[7]; struct stat st; char linebuf[32]; - struct timeval now, then; sigset_t chldmask, savemask; int sshpid, wstatus, binfd, pipefds[2][2]; struct sigaction ignore, saveint, savequit; + ignore.sa_flags = 0; + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); mkdir("o", 0755); CHECK_NE(-1, (lock = open(gc(xasprintf("o/lock.%s", g_hostname)), O_RDWR | O_CREAT, 0644))); CHECK_NE(-1, fcntl(lock, F_SETLKW, &(struct flock){F_WRLCK})); - CHECK_NE(-1, gettimeofday(&now, 0)); - if (!read(lock, &then, 16) || ((now.tv_sec * 1000 + now.tv_usec / 1000) - - (then.tv_sec * 1000 + then.tv_usec / 1000)) >= - (RUNITD_TIMEOUT_MS >> 1)) { - DEBUGF("ssh %s:%hu to spawn %s", g_hostname, g_runitdport, g_runitd); - CHECK_NE(-1, (binfd = open(g_runitd, O_RDONLY | O_CLOEXEC))); - CHECK_NE(-1, fstat(binfd, &st)); - args[0] = "ssh"; - args[1] = "-C"; - args[2] = "-p"; - args[3] = gc(xasprintf("%hu", g_sshport)); - args[4] = g_hostname; - args[5] = gc(MakeDeployScript(ai, st.st_size)); - args[6] = NULL; - ignore.sa_flags = 0; - ignore.sa_handler = SIG_IGN; - LOGIFNEG1(sigemptyset(&ignore.sa_mask)); - LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint)); - LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit)); - LOGIFNEG1(sigemptyset(&chldmask)); - LOGIFNEG1(sigaddset(&chldmask, SIGCHLD)); - LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask)); - CHECK_NE(-1, pipe2(pipefds[0], O_CLOEXEC)); - CHECK_NE(-1, pipe2(pipefds[1], O_CLOEXEC)); - CHECK_NE(-1, (sshpid = fork())); - if (!sshpid) { - sigaction(SIGINT, &saveint, NULL); - sigaction(SIGQUIT, &savequit, NULL); - sigprocmask(SIG_SETMASK, &savemask, NULL); - dup2(pipefds[0][0], 0); - dup2(pipefds[1][1], 1); - execv(g_ssh, args); - _exit(127); - } - LOGIFNEG1(close(pipefds[0][0])); - LOGIFNEG1(close(pipefds[1][1])); - Upload(pipefds[0][1], binfd, &st); - LOGIFNEG1(close(pipefds[0][1])); - CHECK_NE(-1, (got = read(pipefds[1][0], linebuf, sizeof(linebuf)))); - CHECK_GT(got, 0, "on host %s", g_hostname); - linebuf[sizeof(linebuf) - 1] = '\0'; - if (strncmp(linebuf, "ready ", 6) != 0) { - FATALF("expected ready response but got %`'.*s", got, linebuf); - } else { - DEBUGF("got ready response"); - } - g_runitdport = (uint16_t)atoi(&linebuf[6]); - LOGIFNEG1(close(pipefds[1][0])); - CHECK_NE(-1, waitpid(sshpid, &wstatus, 0)); - LOGIFNEG1(sigaction(SIGINT, &saveint, NULL)); - LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL)); - LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL)); - if (WIFEXITED(wstatus)) { - DEBUGF("ssh %s exited with %d", g_hostname, WEXITSTATUS(wstatus)); - } else { - DEBUGF("ssh %s terminated with %s", g_hostname, - strsignal(WTERMSIG(wstatus))); - } - CHECK(WIFEXITED(wstatus) && !WEXITSTATUS(wstatus), "wstatus=%#x", wstatus); - CHECK_NE(-1, gettimeofday(&now, 0)); - CHECK_NE(-1, lseek(lock, 0, SEEK_SET)); - CHECK_NE(-1, write(lock, &now, 16)); - } else { - DEBUGF("nospawn %s on %s:%hu", g_runitd, g_hostname, g_runitdport); + DEBUGF("ssh %s:%hu to spawn %s", g_hostname, g_runitdport, g_runitd); + CHECK_NE(-1, (binfd = open(g_runitd, O_RDONLY | O_CLOEXEC))); + CHECK_NE(-1, fstat(binfd, &st)); + args[0] = "ssh"; + args[1] = "-C"; + args[2] = "-p"; + args[3] = gc(xasprintf("%hu", g_sshport)); + args[4] = g_hostname; + args[5] = gc(MakeDeployScript(ai, st.st_size)); + args[6] = NULL; + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); + CHECK_NE(-1, pipe2(pipefds[0], O_CLOEXEC)); + CHECK_NE(-1, pipe2(pipefds[1], O_CLOEXEC)); + CHECK_NE(-1, (sshpid = fork())); + if (!sshpid) { + sigaction(SIGINT, &(struct sigaction){0}, 0); + sigaction(SIGQUIT, &(struct sigaction){0}, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + dup2(pipefds[0][0], 0); + dup2(pipefds[1][1], 1); + execv(g_ssh, args); + _exit(127); } - CHECK_NE(-1, fcntl(lock, F_SETLK, &(struct flock){F_UNLCK})); - LOGIFNEG1(close(lock)); + close(pipefds[0][0]); + close(pipefds[1][1]); + Upload(pipefds[0][1], binfd, &st); + LOGIFNEG1(close(pipefds[0][1])); + CHECK_NE(-1, (got = read(pipefds[1][0], linebuf, sizeof(linebuf)))); + CHECK_GT(got, 0, "on host %s", g_hostname); + linebuf[sizeof(linebuf) - 1] = '\0'; + if (strncmp(linebuf, "ready ", 6) != 0) { + FATALF("expected ready response but got %`'.*s", got, linebuf); + } else { + DEBUGF("got ready response"); + } + g_runitdport = (uint16_t)atoi(&linebuf[6]); + LOGIFNEG1(close(pipefds[1][0])); + CHECK_NE(-1, waitpid(sshpid, &wstatus, 0)); + LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL)); + if (WIFEXITED(wstatus)) { + DEBUGF("ssh %s exited with %d", g_hostname, WEXITSTATUS(wstatus)); + } else { + DEBUGF("ssh %s terminated with %s", g_hostname, + strsignal(WTERMSIG(wstatus))); + } + CHECK(WIFEXITED(wstatus) && !WEXITSTATUS(wstatus), "wstatus=%#x", wstatus); + LOGIFNEG1(fcntl(lock, F_SETLK, &(struct flock){F_UNLCK})); + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + close(lock); } void Connect(void) { @@ -454,21 +443,21 @@ int SpawnSubprocesses(int argc, char *argv[]) { pids = calloc(argc, sizeof(int)); ignore.sa_flags = 0; ignore.sa_handler = SIG_IGN; - LOGIFNEG1(sigemptyset(&ignore.sa_mask)); - LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint)); - LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit)); - LOGIFNEG1(sigemptyset(&chldmask)); - LOGIFNEG1(sigaddset(&chldmask, SIGCHLD)); - LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); for (i = 0; i < argc; ++i) { args[3] = argv[i]; CHECK_NE(-1, (pids[i] = vfork())); if (!pids[i]) { - xsigaction(SIGINT, SIG_DFL, 0, 0, 0); - xsigaction(SIGQUIT, SIG_DFL, 0, 0, 0); + sigaction(SIGINT, &(struct sigaction){0}, 0); + sigaction(SIGQUIT, &(struct sigaction){0}, 0); sigprocmask(SIG_SETMASK, &savemask, 0); execve(args[0], args, environ); /* for htop */ - _exit(127); + _Exit(127); } } for (exitcode = 0;;) { @@ -493,9 +482,9 @@ int SpawnSubprocesses(int argc, char *argv[]) { break; } } - LOGIFNEG1(sigaction(SIGINT, &saveint, NULL)); - LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL)); - LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL)); + sigprocmask(SIG_SETMASK, &savemask, 0); + sigaction(SIGQUIT, &savequit, 0); + sigaction(SIGINT, &saveint, 0); free(pids); return exitcode; } diff --git a/tool/build/runitd.c b/tool/build/runitd.c index bc2def800..ae7d93228 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -19,10 +19,12 @@ #include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/log/check.h" #include "libc/log/log.h" +#include "libc/macros.internal.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" @@ -98,7 +100,7 @@ char *g_exepath; volatile bool g_interrupted; struct sockaddr_in g_servaddr; unsigned char g_buf[PAGESIZE]; -bool g_daemonize, g_sendready, g_alarmed; +bool g_daemonize, g_sendready; int g_timeout, g_bogusfd, g_servfd, g_clifd, g_exefd; void OnInterrupt(int sig) { @@ -254,18 +256,6 @@ void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf, CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0)); } -void OnAlarm(int sig) { - g_alarmed = true; -} - -void SetDeadline(int seconds, int micros) { - g_alarmed = false; - LOGIFNEG1( - sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, NULL)); - LOGIFNEG1(setitimer( - ITIMER_REAL, &(const struct itimerval){{0, 0}, {seconds, micros}}, NULL)); -} - void Recv(void *p, size_t n) { size_t i, rc; for (i = 0; i < n; i += rc) { @@ -284,12 +274,13 @@ void HandleClient(void) { uint32_t crc; ssize_t got, wrote; struct sockaddr_in addr; + long double now, deadline; sigset_t chldmask, savemask; char *addrstr, *exename, *exe; unsigned char msg[4 + 1 + 4 + 4 + 4]; struct sigaction ignore, saveint, savequit; - int rc, exitcode, wstatus, child, pipefds[2]; uint32_t addrsize, namesize, filesize, remaining; + int rc, events, exitcode, wstatus, child, pipefds[2]; /* read request to run program */ addrsize = sizeof(addr); @@ -326,80 +317,87 @@ void HandleClient(void) { /* run program, tee'ing stderr to both log and client */ DEBUGF("spawning %s", exename); - SetDeadline(DEATH_CLOCK_SECONDS, 0); ignore.sa_flags = 0; ignore.sa_handler = SIG_IGN; - LOGIFNEG1(sigemptyset(&ignore.sa_mask)); - LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint)); - LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit)); - LOGIFNEG1(sigemptyset(&chldmask)); - LOGIFNEG1(sigaddset(&chldmask, SIGCHLD)); - LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); CHECK_NE(-1, pipe2(pipefds, O_CLOEXEC)); CHECK_NE(-1, (child = fork())); if (!child) { - sigaction(SIGINT, &saveint, NULL); - sigaction(SIGQUIT, &savequit, NULL); - sigprocmask(SIG_SETMASK, &savemask, NULL); - /* dup2(g_bogusfd, 0); */ + dup2(g_bogusfd, 0); dup2(pipefds[1], 1); dup2(pipefds[1], 2); - if (pipefds[0] > 2) close(pipefds[1]); - if (g_bogusfd > 2) close(g_bogusfd); + sigaction(SIGINT, &(struct sigaction){0}, 0); + sigaction(SIGQUIT, &(struct sigaction){0}, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); int i = 0; char *args[4] = {0}; args[i++] = g_exepath; if (use_strace) args[i++] = "--strace"; if (use_ftrace) args[i++] = "--ftrace"; execv(g_exepath, args); - _exit(127); + _Exit(127); } - LOGIFNEG1(close(pipefds[1])); + close(pipefds[1]); DEBUGF("communicating %s[%d]", exename, child); - while (!g_alarmed) { - got = read(pipefds[0], g_buf, sizeof(g_buf)); - if (got != -1) { - if (!got) { - close(pipefds[0]); - break; - } - fwrite(g_buf, got, 1, stderr); - SendOutputFragmentMessage(kRunitStderr, g_buf, got); - } else { - CHECK_EQ(EINTR, errno); - } - } + deadline = nowl() + DEATH_CLOCK_SECONDS; for (;;) { - if (g_alarmed) { - WARNF("killing %s which timed out"); - LOGIFNEG1(kill(child, SIGKILL)); - g_alarmed = false; + now = nowl(); + if (now >= deadline) { + WARNF("%s worker timed out", exename); + LOGIFNEG1(kill(child, 9)); + LOGIFNEG1(waitpid(child, 0, 0)); + LOGIFNEG1(close(g_clifd)); + LOGIFNEG1(close(pipefds[0])); + LOGIFNEG1(unlink(g_exepath)); + _exit(1); } - if (waitpid(child, &wstatus, 0) != -1) { + struct pollfd fds[2]; + fds[0].fd = g_clifd; + fds[0].events = POLLIN; + fds[1].fd = pipefds[0]; + fds[1].events = POLLIN; + events = poll(fds, ARRAYLEN(fds), (deadline - now) * 1000); + CHECK_NE(-1, events); // EINTR shouldn't be possible + if (fds[0].revents) { + if (!(fds[0].revents & POLLHUP)) { + WARNF("%s got unexpected input event from client %#x", exename, + fds[0].revents); + } + WARNF("%s client disconnected so killing worker", exename); + LOGIFNEG1(kill(child, 9)); + LOGIFNEG1(waitpid(child, 0, 0)); + LOGIFNEG1(close(g_clifd)); + LOGIFNEG1(close(pipefds[0])); + LOGIFNEG1(unlink(g_exepath)); + _exit(1); + } + got = read(pipefds[0], g_buf, sizeof(g_buf)); + CHECK_NE(-1, got); // EINTR shouldn't be possible + if (!got) { + LOGIFNEG1(close(pipefds[0])); break; - } else { - CHECK_EQ(EINTR, errno); } + fwrite(g_buf, got, 1, stderr); + SendOutputFragmentMessage(kRunitStderr, g_buf, got); } + CHECK_NE(-1, waitpid(child, &wstatus, 0)); // EINTR shouldn't be possible if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus)) { WARNF("%s exited with %d", exename, WEXITSTATUS(wstatus)); } else { - DEBUGF("%s exited with %d", exename, WEXITSTATUS(wstatus)); + VERBOSEF("%s exited with %d", exename, WEXITSTATUS(wstatus)); } exitcode = WEXITSTATUS(wstatus); } else { WARNF("%s terminated with %s", exename, strsignal(WTERMSIG(wstatus))); exitcode = 128 + WTERMSIG(wstatus); } - LOGIFNEG1(sigaction(SIGINT, &saveint, NULL)); - LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL)); - LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL)); - - /* let client know how it went */ - if (unlink(g_exepath) == -1) { - WARNF("failed to delete executable %`'s", g_exepath); - } + LOGIFNEG1(unlink(g_exepath)); SendExitMessage(exitcode); mbedtls_ssl_close_notify(&ezssl); LOGIFNEG1(close(g_clifd)); @@ -429,12 +427,11 @@ TryAgain: int Serve(void) { StartTcpServer(); - sigaction(SIGINT, (&(struct sigaction){.sa_handler = (void *)OnInterrupt}), - NULL); + sigaction(SIGINT, (&(struct sigaction){.sa_handler = OnInterrupt}), 0); sigaction(SIGCHLD, - (&(struct sigaction){.sa_handler = (void *)OnChildTerminated, + (&(struct sigaction){.sa_handler = OnChildTerminated, .sa_flags = SA_RESTART}), - NULL); + 0); for (;;) { if (!Poll() && (!g_timeout || g_interrupted)) break; } @@ -461,10 +458,12 @@ void Daemonize(void) { } int main(int argc, char *argv[]) { + int i; ShowCrashReports(); SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk()); /* __log_level = kLogDebug; */ GetOpts(argc, argv); + for (i = 3; i < 16; ++i) close(i); // poll()'ing /dev/null stdin file descriptor on xnu returns POLLNVAL?! if (IsWindows()) { CHECK_EQ(3, (g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC))); diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 1a4df9a3e..97a2b31f5 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -189,7 +189,7 @@ (runs (format "o/$m/%s.com%s V=5 TESTARGS=-b" name runsuffix)) (buns (format "o/$m/test/%s_test.com%s V=5 TESTARGS=-b" name runsuffix))) (cond ((not (member ext '("c" "cc" "s" "S" "rl" "f"))) - (format "m=%s; make -j8 -O MODE=$m o/$m/%s" + (format "m=%s; make -j12 -O MODE=$m o/$m/%s" mode (directory-file-name (or (file-name-directory @@ -200,7 +200,7 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j8 -O $f MODE=$m") + ,(concat "make -j12 -O $f MODE=$m") "scp $f $f.dbg win7:" "ssh win7 ./%s.com")) mode name (file-name-nondirectory name))) @@ -209,19 +209,19 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j8 -O $f MODE=$m") + ,(concat "make -j12 -O $f MODE=$m") "scp $f $f.dbg win10:" "ssh win10 ./%s.com")) mode name (file-name-nondirectory name))) ((and (equal suffix "") (cosmo-contains "_test." (buffer-file-name))) - (format "m=%s; make -j8 -O MODE=$m %s" + (format "m=%s; make -j12 -O MODE=$m %s" mode runs)) ((and (equal suffix "") (file-exists-p (format "%s" buddy))) (format (cosmo-join " && " - '("m=%s; n=%s; make -j8 -O o/$m/$n%s.o MODE=$m" + '("m=%s; n=%s; make -j12 -O o/$m/$n%s.o MODE=$m" ;; "bloat o/$m/%s.o | head" ;; "nm -C --size o/$m/%s.o | sort -r" "echo" @@ -233,11 +233,11 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j8 -O $f MODE=$m") + ,(concat "make -j12 -O $f MODE=$m") "./$f")) mode name)) ((eq kind 'test) - (format `"m=%s; f=o/$m/%s.com.ok && make -j8 -O $f MODE=$m" mode name)) + (format `"m=%s; f=o/$m/%s.com.ok && make -j12 -O $f MODE=$m" mode name)) ((and (file-regular-p this) (file-executable-p this)) (format "./%s" file)) @@ -246,7 +246,7 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s%s.o" - ,(concat "make -j8 -O $f MODE=$m") + ,(concat "make -j12 -O $f MODE=$m") ;; "nm -C --size $f | sort -r" "echo" "size -A $f | grep '^[.T]' | grep -v 'debug\\|command.line\\|stack' | sort -rnk2" @@ -456,7 +456,7 @@ (error "don't know how to show assembly for non c/c++ source file")) (let* ((default-directory root) (compile-command - (format "make %s -j8 -O MODE=%s %s %s" + (format "make %s -j12 -O MODE=%s %s %s" (or extra-make-flags "") mode asm-gcc asm-clang))) (save-buffer) (set-visited-file-modtime (current-time)) @@ -613,7 +613,7 @@ ((and (eq major-mode 'python-mode) (cosmo-startswith "third_party/python/Lib/test/" file)) (let ((mode (cosmo--make-mode arg))) - (compile (format "make -j8 MODE=%s PYHARNESSARGS=-vv PYTESTARGS=-v o/%s/%s.py.runs" + (compile (format "make -j12 MODE=%s PYHARNESSARGS=-vv PYTESTARGS=-v o/%s/%s.py.runs" mode mode (file-name-sans-extension file))))) ((eq major-mode 'python-mode) (compile (format "python.com %s" file))) diff --git a/tool/net/demo/unix.lua b/tool/net/demo/unix.lua index fc3c6fdcd..f0b3d5147 100644 --- a/tool/net/demo/unix.lua +++ b/tool/net/demo/unix.lua @@ -32,7 +32,31 @@ local function main() -- steal client from redbean fd = GetClientFd() - rc = unix.fork() + rc, errno = unix.fork() + + if errno then + SetStatus(400) + SetHeader('Content-Type', 'text/html; charset=utf-8') + Write('\r\n') + Write('
+ Your redbean doesn't have the ability to fork. Most likely + because you enabled sandboxing and fork() isn't in your bpf + policy. +
+ ]]) + Write('\r\n') + return + end + if rc ~= 0 then unix.close(fd) return diff --git a/tool/net/help.txt b/tool/net/help.txt index 545a363e1..bfd84f8e2 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1539,9 +1539,13 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) + unix.strerrno(errno:int) → str + + Turns `errno` code into its symbolic name, e.g. `"EINTR"`. + unix.strerror(errno:int) → str - Turns `errno` code into a string describing the error. + Turns `errno` code into a longer string describing the error. unix.strsignal(sig:int) → str @@ -1776,10 +1780,10 @@ UNIX MODULE in a supplied path *existed* but wasn't a directory. For example, if you try to `open("foo/bar")` and `foo` is a regular file, then `ENOTDIR` will be returned. Raised by open(), access(), chdir(), - chmod(), chown(), chroot(), execve(), fcntl(), futimesat(), - inotify_add_watch(), link(), mkdir(), mknod(), opendir(), - readlink(), rename(), rmdir(), stat(), symlink(), sysctl(), - truncate(), unlink(), utimensat(), bind(). + chroot(), execve(), link(), mkdir(), mknod(), opendir(), + readlink(), rename(), rmdir(), stat(), symlink(), truncate(), + unlink(), utimensat(), bind(), chmod(), chown(), fcntl(), + futimesat(), inotify_add_watch(). - `EISDIR`: Is a a directory. Raised by copy_file_range(), execve(), open(), read(), rename(), truncate(), unlink(). diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 2870ed9d5..3be047d59 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -536,6 +536,16 @@ static int LuaUnixSetpgid(lua_State *L) { return ReturnRc(L, rc, olderr); } +// unix.setpgrp() → pgid:int[, errno:int] +static int LuaUnixSetpgrp(lua_State *L) { + int rc, pid, pgrp, olderr; + olderr = errno; + pid = luaL_checkinteger(L, 1); + pgrp = luaL_checkinteger(L, 2); + rc = setpgrp(); + return ReturnRc(L, rc, olderr); +} + // unix.setsid() → sid:int[, errno:int] static int LuaUnixSetsid(lua_State *L) { int rc, olderr; @@ -1231,6 +1241,12 @@ static int LuaUnixStrerror(lua_State *L) { return 1; } +// unix.strerrno(errno) → str +static int LuaUnixStrerrno(lua_State *L) { + lua_pushstring(L, strerror_short(luaL_checkinteger(L, 1))); + return 1; +} + // unix.strsignal(sig) → str static int LuaUnixStrsignal(lua_State *L) { lua_pushstring(L, strsignal(luaL_checkinteger(L, 1))); @@ -1506,6 +1522,7 @@ static const luaL_Reg kLuaUnix[] = { {"getpgrp", LuaUnixGetpgrp}, // get process group id {"getpgid", LuaUnixGetpgid}, // get process group id of pid {"setpgid", LuaUnixSetpgid}, // set process group id for pid + {"setpgrp", LuaUnixSetpgrp}, // sets process group id {"getsid", LuaUnixGetsid}, // get session id of pid {"setsid", LuaUnixSetsid}, // create a new session id {"getpid", LuaUnixGetpid}, // get id of this process @@ -1533,6 +1550,7 @@ static const luaL_Reg kLuaUnix[] = { {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock {"strerror", LuaUnixStrerror}, // turn errno into string + {"strerrno", LuaUnixStrerrno}, // turn errno into string {"strsignal", LuaUnixStrsignal}, // turn signal into string {0}, // }; @@ -1571,6 +1589,7 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "O_EXCL", O_EXCL); // LuaSetIntField(L, "O_TRUNC", O_TRUNC); // LuaSetIntField(L, "O_CLOEXEC", O_CLOEXEC); // + LuaSetIntField(L, "O_DIRECT", O_DIRECT); // no-op on xnu/openbsd LuaSetIntField(L, "O_APPEND", O_APPEND); // weird on nt LuaSetIntField(L, "O_TMPFILE", O_TMPFILE); // linux, windows LuaSetIntField(L, "O_NOFOLLOW", O_NOFOLLOW); // unix @@ -1616,6 +1635,7 @@ int LuaUnix(lua_State *L) { // rlimit() resources LuaSetIntField(L, "RLIMIT_AS", RLIMIT_AS); + LuaSetIntField(L, "RLIMIT_RSS", RLIMIT_RSS); LuaSetIntField(L, "RLIMIT_CPU", RLIMIT_CPU); LuaSetIntField(L, "RLIMIT_FSIZE", RLIMIT_FSIZE); LuaSetIntField(L, "RLIMIT_NPROC", RLIMIT_NPROC); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 4dfbf6724..8d1f76780 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -176,8 +176,8 @@ STATIC_YOINK("zip_uri_support"); #define VERSION 0x010500 #define HEARTBEAT 5000 /*ms*/ #define HASH_LOAD_FACTOR /* 1. / */ 4 -#define read(F, P, N) readv(F, &(struct iovec){P, N}, 1) -#define write(F, P, N) writev(F, &(struct iovec){P, N}, 1) +#define READ(F, P, N) readv(F, &(struct iovec){P, N}, 1) +#define WRITE(F, P, N) writev(F, &(struct iovec){P, N}, 1) #define LockInc(P) asm volatile("lock incq\t%0" : "=m"(*(P))) #define AppendCrlf(P) mempcpy(P, "\r\n", 2) #define HasHeader(H) (!!msg.headers[H].a) @@ -1029,7 +1029,7 @@ static void Daemonize(void) { umask(0); if (pidpath) { fd = open(pidpath, O_CREAT | O_WRONLY, 0644); - write(fd, ibuf, uint64toarray_radix10(getpid(), ibuf)); + WRITE(fd, ibuf, uint64toarray_radix10(getpid(), ibuf)); close(fd); } if (!logpath) ProgramLogPath("/dev/null"); @@ -1253,7 +1253,7 @@ static ssize_t ReadAll(int fd, char *p, size_t n) { ssize_t rc; size_t i, got; for (i = 0; i < n;) { - rc = read(fd, p + i, n - i); + rc = READ(fd, p + i, n - i); if (rc != -1) { got = rc; i += got; @@ -2727,7 +2727,7 @@ static void LaunchBrowser(const char *path) { sigprocmask(SIG_BLOCK, &chldmask, &savemask); CHECK_NE(-1, (pid = fork())); if (!pid) { - setpgid(getpid(), getpid()); // ctrl-c'ing redbean shouldn't kill browser + setpgrp(); // ctrl-c'ing redbean shouldn't kill browser sigaction(SIGINT, &saveint, 0); sigaction(SIGQUIT, &savequit, 0); sigprocmask(SIG_SETMASK, &savemask, 0); @@ -3396,13 +3396,10 @@ static void StoreAsset(char *path, size_t pathlen, char *data, size_t datalen, uint16_t gflags, iattrs, mtime, mdate, dosmode, method, disk; size_t oldcdirsize, oldcdiroffset, records, cdiroffset, cdirsize, complen, uselen; - if (IsOpenbsd() || IsNetbsd() || IsWindows()) { DIEF("(cfg) StoreAsset() not available on Windows/NetBSD/OpenBSD yet"); } - INFOF("Storing asset %`'s", path); - disk = gflags = iattrs = 0; if (_isutf8(path, pathlen)) gflags |= kZipGflagUtf8; if (_istext(data, datalen)) iattrs |= kZipIattrText; @@ -3870,7 +3867,7 @@ static int LuaFetch(lua_State *L) { LuaThrowTlsError(L, "write", ret); unreachable; } - } else if (write(sock, request, requestlen) != requestlen) { + } else if (WRITE(sock, request, requestlen) != requestlen) { close(sock); luaL_error(L, "write error: %s", strerror(errno)); unreachable; @@ -3904,7 +3901,7 @@ static int LuaFetch(lua_State *L) { unreachable; } } - } else if ((rc = read(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) { + } else if ((rc = READ(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) { close(sock); free(inbuf.p); DestroyHttpMessage(&msg); @@ -6863,7 +6860,7 @@ static void RestoreApe(void) { if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); - if ((zfd = OpenExecutable()) == -1 || write(zfd, p, n) == -1) + if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) WARNF("(srvr) can't restore .ape"); free(p); } else { @@ -7197,7 +7194,7 @@ void RedBean(int argc, char *argv[]) { // ctrl-c isn't propagating as expected when running redbean // underneath strace.com :| if (!IsWindows()) { - setpgid(getpid(), getpid()); + setpgrp(); } if (logpath) { close(2);