From 425c055116d26cd424114b2acd850a027157ad42 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 6 Sep 2023 22:39:36 -0700 Subject: [PATCH] Make improvements - Polyfill readlink("foo/") dir check on Windows - Support asynchronous signal delivery on Windows - Restore Windows Console from execve() daisy chain - Work around bug in AARCH64 Optimized Routines memcmp() - Disable unbourne.com shell completion on Windows for now - Don't always set virtual terminal input state on console - Remove Musl Libc's unusual preservation of realpath("//") - Make realpath() strongly link malloc() to pass configure test - Delete cosh.com shell, now that unbourne.com works on Windows! --- examples/cosh.c | 318 ------------------ examples/ctrlc.c | 31 +- examples/ttyinfo.c | 2 +- examples/unbourne.c | 7 +- libc/calls/calls.h | 2 +- libc/calls/calls.mk | 7 + libc/calls/execve-nt.greg.c | 11 +- libc/calls/isatty-nt.c | 9 +- libc/calls/mkntpath.c | 1 + libc/calls/onntconsoleevent.c | 136 ++++++-- libc/calls/onntconsoleevent_init.S | 8 +- libc/calls/read-nt.c | 5 +- libc/calls/readlinkat-nt.c | 60 +++- libc/calls/readlinkat.c | 14 +- libc/calls/sigwinch-nt.c | 2 - libc/calls/tcsetattr-nt.c | 15 +- libc/calls/winstdin1.c | 35 +- .../clone-nt.S => calls/winthreadlaunch.S} | 6 +- libc/intrin/aarch64/memcmp.S | 218 ------------ libc/intrin/exit.c | 4 - libc/intrin/memcmp.c | 3 - libc/intrin/pthreadlist.c | 22 ++ libc/intrin/pthreadlock.c | 22 ++ libc/{calls => mem}/realpath.c | 34 +- libc/nt/enum/context.h | 21 ++ libc/nt/kernel32/GetThreadContext.S | 18 + libc/nt/kernel32/InitializeContext.S | 18 + libc/nt/kernel32/ResumeThread.S | 20 ++ libc/nt/master.sh | 5 +- libc/nt/thread.h | 5 + libc/runtime/enable_tls.c | 2 - libc/runtime/fork-nt.c | 6 +- libc/runtime/winmain.greg.c | 105 +++--- libc/sock/wsablock.c | 16 +- libc/str/str.mk | 6 + libc/str/tprecode8to16.c | 8 +- test/libc/calls/readlinkat_test.c | 4 +- test/libc/mem/realpath_test.c | 75 +++++ third_party/finger/finger.c | 1 + tool/build/apelink.c | 5 +- 40 files changed, 581 insertions(+), 706 deletions(-) delete mode 100644 examples/cosh.c rename libc/{runtime/clone-nt.S => calls/winthreadlaunch.S} (96%) delete mode 100644 libc/intrin/aarch64/memcmp.S create mode 100644 libc/intrin/pthreadlist.c create mode 100644 libc/intrin/pthreadlock.c rename libc/{calls => mem}/realpath.c (85%) create mode 100644 libc/nt/enum/context.h create mode 100644 libc/nt/kernel32/GetThreadContext.S create mode 100644 libc/nt/kernel32/InitializeContext.S create mode 100644 libc/nt/kernel32/ResumeThread.S create mode 100644 test/libc/mem/realpath_test.c diff --git a/examples/cosh.c b/examples/cosh.c deleted file mode 100644 index 87736909d..000000000 --- a/examples/cosh.c +++ /dev/null @@ -1,318 +0,0 @@ -#if 0 -/*─────────────────────────────────────────────────────────────────╗ -│ To the extent possible under law, Justine Tunney has waived │ -│ all copyright and related or neighboring rights to this file, │ -│ as it is written in the following disclaimers: │ -│ • http://unlicense.org/ │ -│ • http://creativecommons.org/publicdomain/zero/1.0/ │ -╚─────────────────────────────────────────────────────────────────*/ -#endif -#include "libc/calls/calls.h" -#include "libc/calls/struct/dirent.h" -#include "libc/calls/struct/rusage.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/timespec.h" -#include "libc/errno.h" -#include "libc/fmt/fmt.h" -#include "libc/fmt/itoa.h" -#include "libc/limits.h" -#include "libc/log/appendresourcereport.internal.h" -#include "libc/log/internal.h" -#include "libc/log/log.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" -#include "libc/runtime/internal.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/append.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/clock.h" -#include "libc/sysv/consts/dt.h" -#include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/sig.h" -#include "libc/time/time.h" -#include "libc/x/x.h" -#include "third_party/linenoise/linenoise.h" - -/** - * @fileoverview Cosmopolitan Shell - * - * This doesn't have script language features like UNBOURNE.COM but it - * works on Windows and, unlike CMD.EXE, has CTRL-P, CTRL-R, and other - * GNU Emacs / Readline keyboard shortcuts. - * - * One day we'll have UNBOURNE.COM working on Windows but the code isn't - * very maintainable sadly. - */ - -volatile int gotint; - -static void OnInterrupt(int sig) { - gotint = sig; -} - -static void AddUniqueCompletion(linenoiseCompletions *c, char *s) { - size_t i; - if (!s) return; - for (i = 0; i < c->len; ++i) { - if (!strcmp(s, c->cvec[i])) { - return; - } - } - c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec)); - c->cvec[c->len - 1] = s; -} - -static void CompleteFilename(const char *p, const char *q, const char *b, - linenoiseCompletions *c) { - DIR *d; - char *buf; - const char *g; - struct dirent *e; - if ((buf = malloc(512))) { - if ((g = memrchr(p, '/', q - p))) { - *(char *)mempcpy(buf, p, MIN(g - p, 511)) = 0; - p = ++g; - } else { - strcpy(buf, "."); - } - if ((d = opendir(buf))) { - while ((e = readdir(d))) { - if (!strcmp(e->d_name, ".")) continue; - if (!strcmp(e->d_name, "..")) continue; - if (!strncmp(e->d_name, p, q - p)) { - snprintf(buf, 512, "%.*s%s%s", p - b, b, e->d_name, - e->d_type == DT_DIR ? "/" : ""); - AddUniqueCompletion(c, strdup(buf)); - } - } - closedir(d); - } - free(buf); - } -} - -static void ShellCompletion(const char *p, linenoiseCompletions *c) { - const char *q, *b; - for (b = p, q = (p += strlen(p)); p > b; --p) { - if (!isalnum(p[-1]) && - (p[-1] != '.' && p[-1] != '_' && p[-1] != '-' && p[-1] != '+' && - p[-1] != '[' && p[-1] != '/' && p[-1] != '\\')) { - break; - } - } - CompleteFilename(p, q, b, c); -} - -static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) { - char *h = 0; - linenoiseCompletions c = {0}; - ShellCompletion(p, &c); - if (c.len == 1) { - h = strdup(c.cvec[0] + strlen(p)); - } - linenoiseFreeCompletions(&c); - return h; -} - -static char *MakePrompt(char *p) { - char *s, buf[256]; - if (!gethostname(buf, sizeof(buf))) { - p = stpcpy(p, "\e[95m"); - if ((s = getenv("USER"))) { - p = stpcpy(p, s); - *p++ = '@'; - } - p = stpcpy(p, buf); - *p++ = ':'; - } - p = stpcpy(p, "\e[96m"); - if ((s = getcwd(buf, sizeof(buf)))) { - p = stpcpy(p, s); - } - return stpcpy(p, "\e[0m>: "); -} - -int main(int argc, char *argv[]) { - bool timeit; - int64_t nanos; - struct rusage ru; - struct timespec ts1, ts2; - char *prog, path[PATH_MAX]; - sigset_t chldmask, savemask; - int stdoutflags, stderrflags; - int n, rc, ws, child, killcount; - const char *stdoutpath, *stderrpath; - struct sigaction sa, saveint, savequit; - char *p, *line, **args, *arg, *start, *state, prompt[1024]; - linenoiseSetFreeHintsCallback(free); - linenoiseSetHintsCallback(ShellHint); - linenoiseSetCompletionCallback(ShellCompletion); - MakePrompt(prompt); - while ((line = linenoiseWithHistory(prompt, "cmd"))) { - n = 0; - start = line; - if (_startswith(start, "time ")) { - timeit = true; - start += 5; - } else { - timeit = false; - } - stdoutpath = 0; - stderrpath = 0; - stdoutflags = 0; - stderrflags = 0; - args = xcalloc(1, sizeof(*args)); - while ((arg = strtok_r(start, " \t\r\n", &state))) { - // cmd >>stdout.txt - if (arg[0] == '>' && arg[1] == '>') { - stdoutflags = O_WRONLY | O_APPEND | O_CREAT; - stdoutpath = arg + 2; - } else if (arg[0] == '>') { - // cmd >stdout.txt - stdoutflags = O_WRONLY | O_CREAT | O_TRUNC; - stdoutpath = arg + 1; - } else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') { - // cmd 2>>stderr.txt - stderrflags = O_WRONLY | O_APPEND | O_CREAT; - stderrpath = arg + 3; - } else if (arg[0] == '2' && arg[1] == '>') { - // cmd 2>stderr.txt - stderrflags = O_WRONLY | O_CREAT | O_TRUNC; - stderrpath = arg + 2; - } else { - // arg - args = xrealloc(args, (++n + 1) * sizeof(*args)); - args[n - 1] = arg; - args[n - 0] = 0; - } - start = 0; - } - if (n > 0) { - if ((prog = commandv(args[0], path, sizeof(path)))) { - - // let keyboard interrupts kill child and not shell - gotint = 0; - killcount = 0; - sa.sa_flags = 0; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sigaction(SIGQUIT, &sa, &savequit); - sa.sa_handler = OnInterrupt; - sigaction(SIGINT, &sa, &saveint); - sigemptyset(&chldmask); - sigaddset(&chldmask, SIGCHLD); - sigprocmask(SIG_BLOCK, &chldmask, &savemask); - - // record timestamp - if (timeit) { - clock_gettime(CLOCK_REALTIME, &ts1); - } - - // launch process - if (!(child = vfork())) { - if (stdoutpath) { - close(1); - open(stdoutpath, stdoutflags, 0644); - } - if (stderrpath) { - close(2); - open(stderrpath, stderrflags, 0644); - } - sigaction(SIGINT, &saveint, 0); - sigaction(SIGQUIT, &savequit, 0); - sigprocmask(SIG_SETMASK, &savemask, 0); - execv(prog, args); - _Exit(127); - } - - // wait for process - for (;;) { - if (gotint) { - switch (killcount) { - case 0: - // ctrl-c - // we do nothing - // terminals broadcast sigint to process group - rc = 0; - break; - case 1: - // ctrl-c ctrl-c - // we try sending sigterm - rc = kill(child, SIGTERM); - break; - default: - // ctrl-c ctrl-c ctrl-c ... - // we use kill -9 as our last resort - rc = kill(child, SIGKILL); - break; - } - if (rc == -1) { - fprintf(stderr, "kill failed: %m\n"); - exit(1); - } - ++killcount; - gotint = 0; - } - rc = wait4(0, &ws, 0, &ru); - if (rc != -1) { - break; - } else if (errno == EINTR) { - errno = 0; - } else { - fprintf(stderr, "wait failed: %m\n"); - exit(1); - } - } - - // print resource consumption for `time` pseudocommand - if (timeit) { - clock_gettime(CLOCK_REALTIME, &ts2); - if (ts2.tv_sec == ts1.tv_sec) { - nanos = ts2.tv_nsec - ts1.tv_nsec; - } else { - nanos = (ts2.tv_sec - ts1.tv_sec) * 1000000000LL; - nanos += 1000000000LL - ts1.tv_nsec; - nanos += ts2.tv_nsec; - } - printf("took %,ldµs wall time\n", nanos / 1000); - p = 0; - AppendResourceReport(&p, &ru, "\n"); - fputs(p, stdout); - free(p); - } - - // update prompt to reflect exit status - p = prompt; - if (WIFEXITED(ws)) { - if (WEXITSTATUS(ws)) { - if (!__nocolor) p = stpcpy(p, "\e[1;31m"); - p = stpcpy(p, "rc="); - p = FormatInt32(p, WEXITSTATUS(ws)); - if (128 < WEXITSTATUS(ws) && WEXITSTATUS(ws) <= 128 + 32) { - *p++ = ' '; - p = stpcpy(p, strsignal(WEXITSTATUS(ws) - 128)); - } - if (!__nocolor) p = stpcpy(p, "\e[0m"); - *p++ = ' '; - } - } else { - if (!__nocolor) p = stpcpy(p, "\e[1;31m"); - p = stpcpy(p, strsignal(WTERMSIG(ws))); - if (!__nocolor) p = stpcpy(p, "\e[0m"); - *p++ = ' '; - } - MakePrompt(p); - - sigprocmask(SIG_SETMASK, &savemask, 0); - sigaction(SIGINT, &saveint, 0); - sigaction(SIGQUIT, &savequit, 0); - } else { - fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]); - } - } - free(line); - free(args); - } -} diff --git a/examples/ctrlc.c b/examples/ctrlc.c index 8fa769f2f..86e532714 100644 --- a/examples/ctrlc.c +++ b/examples/ctrlc.c @@ -10,14 +10,25 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" +#include "libc/limits.h" #include "libc/runtime/runtime.h" +#include "libc/sock/struct/pollfd.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/sysv/consts/limits.h" +#include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" +volatile bool gotsig; + void SignalHandler(int sig) { // we don't need to do anything in our signal handler since the signal // delivery itself causes a visible state change, saying what happened + const char *s = "SignalHandler() called\n"; + write(1, s, strlen(s)); // notice both functions are @asyncsignalsafe + // however this will help if delivered asynchronously in cpubound code + // it's also necessary to discern spurious interrupts from real signal + gotsig = true; } int main(int argc, char *argv[]) { @@ -31,6 +42,16 @@ int main(int argc, char *argv[]) { for (;;) { + // some programs are blocked on cpu rather than i/o + // such programs shall rely on asynchronous signals + printf("doing cpu task...\n"); + for (volatile int i = 0; i < INT_MAX / 20; ++i) { + if (gotsig) { + printf("\rgot ctrl+c asynchronously\n"); + exit(0); + } + } + // posix guarantees atomic i/o if you use pipe_buf sized buffers // that way we don't need to worry about things like looping and // we can also be assured that multiple actors wont have tearing @@ -45,6 +66,7 @@ int main(int argc, char *argv[]) { // it's possible to be more precise if we were building library // code. for example, you can block signals using sigprocmask() // and then use pselect() to do the waiting. + printf("doing read i/o task...\n"); int got = read(0, buf, sizeof(buf)); // check if the read operation failed @@ -59,8 +81,13 @@ int main(int argc, char *argv[]) { // however EINTR is very useful, when we choose to use it // the \r character is needed so when the line is printed // it'll overwrite the ^C that got echo'd with the ctrl-c - printf("\rgot ctrl+c\n"); - exit(0); + if (gotsig) { + printf("\rgot ctrl+c via i/o eintr\n"); + exit(0); + } else { + printf("\rgot spurious eintr\n"); + continue; + } } else { // log it in the unlikely event something else went wrong perror(""); diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index 9938c259c..1bfd871ab 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -55,7 +55,7 @@ int rawmode(void) { static bool once; struct termios t; if (!once) { - if (tcgetattr(1, &oldterm)) { + if (!tcgetattr(1, &oldterm)) { atexit(restoretty); } else { perror("tcgetattr"); diff --git a/examples/unbourne.c b/examples/unbourne.c index 6bef9907b..f85709c00 100644 --- a/examples/unbourne.c +++ b/examples/unbourne.c @@ -5816,8 +5816,11 @@ static ssize_t preadfd(void) { retry: if (!parsefile->fd && isatty(0)) { linenoiseSetFreeHintsCallback(free); - linenoiseSetHintsCallback(ShellHint); - linenoiseSetCompletionCallback(ShellCompletion); + if (!IsWindows()) { + // TODO(jart): Cache $PATH search. + linenoiseSetHintsCallback(ShellHint); + linenoiseSetCompletionCallback(ShellCompletion); + } if ((p = linenoiseWithHistory(">: ", "unbourne"))) { nr = min(strlen(p), IBUFSIZ - 2); memcpy(buf, p, nr); diff --git a/libc/calls/calls.h b/libc/calls/calls.h index c96e4b4ad..43e240d8c 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -73,7 +73,7 @@ typedef int sig_atomic_t; bool32 isatty(int); char *getcwd(char *, size_t); -char *realpath(const char *, char *); +char *realpath(const char *, char *) __wur; char *ttyname(int); int access(const char *, int) dontthrow; int chdir(const char *); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 9df4e65a7..7c6c3b583 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,6 +65,13 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) +# we can't use sanitizers because: +# we're on a stack owned by win32 without tls +o/$(MODE)/libc/calls/onntconsoleevent.o: private \ + COPTS += \ + -ffreestanding \ + -fno-sanitize=all + # we can't use asan because: # siginfo_t memory is owned by kernels o/$(MODE)/libc/calls/siginfo2cosmo.o: private \ diff --git a/libc/calls/execve-nt.greg.c b/libc/calls/execve-nt.greg.c index 0f0ee1fbe..8942e18fd 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/calls/execve-nt.greg.c @@ -38,6 +38,7 @@ #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" +#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" @@ -56,10 +57,10 @@ extern long __klog_handle; __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; -__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; __msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; -__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; __msabi extern typeof(TerminateThread) *const __imp_TerminateThread; +__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; +__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; wontreturn void __switch_stacks(intptr_t, long, long, long, void (*)(intptr_t, intptr_t, long, long), @@ -70,9 +71,9 @@ __msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) { } static keywords void PurgeHandle(intptr_t h) { - if (h && h != -1) { - __imp_CloseHandle(h); - } + if (!h) return; + if (h == -1) return; + __imp_CloseHandle(h); } static keywords void PurgeThread(intptr_t h) { diff --git a/libc/calls/isatty-nt.c b/libc/calls/isatty-nt.c index f251c1f5f..261725194 100644 --- a/libc/calls/isatty-nt.c +++ b/libc/calls/isatty-nt.c @@ -17,15 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -#include "libc/nt/enum/filetype.h" -#include "libc/nt/files.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/nt/console.h" #include "libc/sysv/errfuns.h" textwindows bool32 sys_isatty_nt(int fd) { if (__isfdopen(fd)) { - if (__isfdkind(fd, kFdConsole) || - (__isfdkind(fd, kFdFile) && - GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar)) { + uint32_t mode; + if (GetConsoleMode(g_fds.p[fd].handle, &mode)) { return true; } else { enotty(); diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index 3948cafb8..60fdd2ae4 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -20,6 +20,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" #include "libc/nt/systeminfo.h" diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c index 3d6cb2518..489413db0 100644 --- a/libc/calls/onntconsoleevent.c +++ b/libc/calls/onntconsoleevent.c @@ -17,49 +17,143 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/sig.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/strace.internal.h" +#include "libc/intrin/dll.h" +#include "libc/log/libfatal.internal.h" #include "libc/nexgen32e/nt2sysv.h" +#include "libc/nt/enum/context.h" #include "libc/nt/enum/ctrlevent.h" +#include "libc/nt/enum/threadaccess.h" +#include "libc/nt/runtime.h" +#include "libc/nt/struct/context.h" #include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/str/str.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" #include "libc/thread/tls2.internal.h" #ifdef __x86_64__ -textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { - struct CosmoTib tib; +// WIN32 doesn't have the System V red-zone. Microsoft says they reserve +// the right to trample all over it. so we technically don't need to use +// this value. it's just not clear how common it is for WIN32 to clobber +// the red zone, which means broken code could seem to mostly work which +// means it's better that we're not the ones responsible for breaking it +#define kRedzoneSize 128 - // win32 spawns a thread on its own just to deliver sigint - // TODO(jart): make signal code lockless so we can delete! - if (__tls_enabled && !__get_tls_win32()) { - bzero(&tib, sizeof(tib)); - tib.tib_self = &tib; - tib.tib_self2 = &tib; - atomic_store_explicit(&tib.tib_tid, GetCurrentThreadId(), - memory_order_relaxed); - __set_tls_win32(&tib); - } +// Both Microsoft and the Fifth Bell System agree on this one. +#define kStackAlign 16 - STRACE("__onntconsoleevent(%u)", dwCtrlType); +__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; +__msabi extern typeof(GetLastError) *const __imp_GetLastError; +__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; +__msabi extern typeof(GetThreadContext) *const __imp_GetThreadContext; +__msabi extern typeof(OpenThread) *const __imp_OpenThread; +__msabi extern typeof(ResumeThread) *const __imp_ResumeThread; +__msabi extern typeof(SuspendThread) *const __imp_SuspendThread; +__msabi extern typeof(WriteFile) *const __imp_WriteFile; + +int WinThreadLaunch(int, int, int (*)(int, int), intptr_t); + +static unsigned long StrLen(const char *s) { + unsigned long n = 0; + while (*s++) ++n; + return n; +} + +static void Log(const char *s) { +#ifndef NDEBUG + __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); +#endif +} + +static int GetSig(uint32_t dwCtrlType) { switch (dwCtrlType) { case kNtCtrlCEvent: - __sig_add(0, SIGINT, SI_KERNEL); - return true; + return SIGINT; case kNtCtrlBreakEvent: - __sig_add(0, SIGQUIT, SI_KERNEL); - return true; + return SIGQUIT; case kNtCtrlCloseEvent: case kNtCtrlLogoffEvent: // only received by services case kNtCtrlShutdownEvent: // only received by services - __sig_add(0, SIGHUP, SI_KERNEL); - return true; + return SIGHUP; default: - return false; + __builtin_unreachable(); } } +__msabi textwindows dontinstrument dontasan dontubsan bool32 +__onntconsoleevent(uint32_t dwCtrlType) { + + // the signal to be delivered + int sig = GetSig(dwCtrlType); + int sic = SI_KERNEL; + + // if we don't have tls, then we can't hijack a safe stack from a + // thread so just try our luck punting the signal to the next i/o + if (!__tls_enabled) { + goto PuntSignal; + } + + // we're on a stack that's owned by win32. to make matters worse, + // win32 spawns a totally new thread just to invoke this handler. + // that means most of the cosmo runtime is broken right now which + // means we can't call the user signal handler safely. what we'll + // do instead is pick a posix thread at random to hijack, pretend + // to be that thread, use its stack, and then deliver this signal + // asynchronously if it isn't blocked. hopefully it won't longjmp + // because once the handler returns, we'll restore the old thread + bool gotsome = false; + pthread_spin_lock(&_pthread_lock); + for (struct Dll *e = dll_first(_pthread_list); e && !gotsome; + e = dll_next(_pthread_list, e)) { + struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); + int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); + if (tid <= 0) continue; // -1 means spawning, 0 means terminated + if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // blocked + intptr_t th; + if ((th = __imp_OpenThread(kNtThreadSuspendResume | kNtThreadGetContext, + false, tid))) { + uint32_t old_suspend_count = __imp_SuspendThread(th); + if (old_suspend_count != -1u) { + if (!old_suspend_count && + atomic_load_explicit(&pt->status, memory_order_acquire) < + kPosixThreadTerminated) { + struct NtContext ctx; + __repstosb(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = kNtContextControl; + if (__imp_GetThreadContext(th, &ctx)) { + gotsome = true; + pthread_spin_unlock(&_pthread_lock); + __set_tls_win32(pt->tib); + WinThreadLaunch(sig, sic, __sig_raise, + ROUNDDOWN(ctx.Rsp - kRedzoneSize, kStackAlign) - 8); + } else { + Log("GetThreadContext failed\n"); + } + } + __imp_ResumeThread(th); + } else { + Log("SuspendThread failed\n"); + } + __imp_CloseHandle(th); + } else { + Log("OpenThread failed\n"); + } + } + + if (!gotsome) { + pthread_spin_unlock(&_pthread_lock); + PuntSignal: + __sig_add(0, sig, sic); + } + return true; +} + #endif /* __x86_64__ */ diff --git a/libc/calls/onntconsoleevent_init.S b/libc/calls/onntconsoleevent_init.S index 1f22620d9..3e5d06a2f 100644 --- a/libc/calls/onntconsoleevent_init.S +++ b/libc/calls/onntconsoleevent_init.S @@ -18,17 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" #include "libc/macros.internal.h" -.text.windows - -__onntconsoleevent_nt: - ezlea __onntconsoleevent,ax - jmp __nt2sysv - .endfn __onntconsoleevent_nt,globl,hidden .init.start 300,_init_onntconsoleevent testb IsWindows() jz 1f - ezlea __onntconsoleevent_nt,cx + ezlea __onntconsoleevent,cx pushpop 1,%rdx ntcall __imp_SetConsoleCtrlHandler 1: .init.end 300,_init_onntconsoleevent,globl,hidden diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index ed6e25660..c34cc15ad 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -28,7 +28,6 @@ #include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" -#include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/nt/console.h" @@ -107,6 +106,7 @@ StartOver: // since for overlapped i/o, we always use GetOverlappedResult ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) { + TryAgain: // the i/o operation is in flight; blocking is unavoidable // if we're in a non-blocking mode, then immediately abort // if an interrupt is pending then we abort before waiting @@ -137,6 +137,9 @@ StartOver: // overlapped is allocated on stack, so it's important we wait // for windows to acknowledge that it's done using that memory ok = GetOverlappedResult(handle, &overlap, &got, true); + if (!ok && GetLastError() == kNtErrorIoIncomplete) { + goto TryAgain; + } } CloseHandle(overlap.hEvent); } else { diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index a97aecd56..e85de99d9 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -20,8 +20,11 @@ #include "libc/intrin/strace.internal.h" #include "libc/mem/alloca.h" #include "libc/nt/createfile.h" +#include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" #include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/filesharemode.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/enum/fsctl.h" #include "libc/nt/enum/io.h" #include "libc/nt/files.h" @@ -34,28 +37,45 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, size_t bufsiz) { + + char16_t path16[PATH_MAX]; + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; + size_t len = strlen16(path16); + bool must_be_directory = len > 1 && path16[len - 1] == '\\'; + if (must_be_directory) path16[--len] = 0; + int64_t h; ssize_t rc; - uint64_t w; - wint_t x, y; - 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; - mem = 16384; - memory = alloca(mem); + uint32_t mem = 16384; + volatile char *memory = alloca(mem); CheckLargeStackAllocation((char *)memory, mem); - rdb = (struct NtReparseDataBuffer *)memory; - if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting, + struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory; + if ((h = CreateFile(path16, kNtFileReadAttributes, + kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, + 0, kNtOpenExisting, kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics, 0)) != -1) { - if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &n, 0)) { + + // if path had trailing slash, assert last component is directory + if (must_be_directory) { + struct NtByHandleFileInformation wst; + if (GetFileType(h) != kNtFileTypeDisk || + (GetFileInformationByHandle(h, &wst) && + !(wst.dwFileAttributes & kNtFileAttributeDirectory))) { + return enotdir(); + } + } + + uint32_t bc; + if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &bc, 0)) { if (rdb->ReparseTag == kNtIoReparseTagSymlink) { - i = 0; - j = 0; - n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); - p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + + + uint32_t i = 0; + uint32_t j = 0; + uint32_t n = + rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); + char16_t *p = + (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset); if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { p[1] = p[0]; @@ -63,15 +83,18 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, p[2] = '/'; } while (i < n) { - x = p[i++] & 0xffff; + + wint_t x = p[i++] & 0xffff; if (!IsUcs2(x)) { if (i < n) { - y = p[i++] & 0xffff; + wint_t y = p[i++] & 0xffff; x = MergeUtf16(x, y); } else { x = 0xfffd; } } + + uint64_t w; if (x < 0200) { if (x == '\\') { x = '/'; @@ -95,6 +118,7 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, } else { rc = -1; } + CloseHandle(h); } else { rc = __fix_enotdir(-1, path16); diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index d8ff7f258..7ffeb57cf 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -24,8 +24,8 @@ #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/runtime/runtime.h" -#include "libc/sysv/errfuns.h" #include "libc/runtime/zipos.internal.h" +#include "libc/sysv/errfuns.h" /** * Reads symbolic link. @@ -40,12 +40,20 @@ * 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 + * @raise EINVAL if path isn't a symbolic link + * @raise ENOENT if `path` didn't exist + * @raise ENOTDIR if parent component existed that's not a directory + * @raise ENOTDIR if base component ends with slash and is not a dir + * @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX` + * @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX` + * @raise EBADF on relative `path` when `dirfd` isn't open or `AT_FDCWD` + * @raise ELOOP if a loop was detected resolving parent components * @asyncsignalsafe */ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { ssize_t bytes; - if ((IsAsan() && !__asan_is_valid(buf, bufsiz)) || (bufsiz && !buf)) { + if ((bufsiz && !buf) || (IsAsan() && (!__asan_is_valid_str(path) || + !__asan_is_valid(buf, bufsiz)))) { bytes = efault(); } else if (_weaken(__zipos_notat) && (bytes = __zipos_notat(dirfd, path)) == -1) { diff --git a/libc/calls/sigwinch-nt.c b/libc/calls/sigwinch-nt.c index 7d6cf3aa4..ac17c68d4 100644 --- a/libc/calls/sigwinch-nt.c +++ b/libc/calls/sigwinch-nt.c @@ -24,7 +24,6 @@ #include "libc/calls/struct/winsize.internal.h" #include "libc/dce.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/strace.internal.h" #include "libc/nt/console.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" #include "libc/sysv/consts/sicode.h" @@ -66,7 +65,6 @@ __attribute__((__constructor__)) static void sigwinch_init(void) { if (!IsWindows()) return; unsigned ws = __get_console_size(); atomic_store_explicit(&__win_winsize, ws, memory_order_release); - STRACE("sigwinch_init() → %08x", ws); } #endif /* __x86_64__ */ diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index 6681fd690..ddebd6d04 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -185,18 +185,23 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { if (opt == TCSAFLUSH) { FlushConsoleInputBuffer(hInput); } - inmode &= - ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); + inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | + kNtEnableProcessedInput | kNtEnableVirtualTerminalInput); inmode |= kNtEnableWindowInput; __ttymagic = 0; if (tio->c_lflag & ICANON) { - inmode |= kNtEnableLineInput; + inmode |= kNtEnableLineInput | kNtEnableProcessedInput; } else { __ttymagic |= kFdTtyMunging; if (tio->c_cc[VMIN] != 1) { STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows"); return einval(); } + if (IsAtLeastWindows10()) { + // - keys like f1, up, etc. get turned into \e ansi codes + // - totally destroys default console gui (e.g. up arrow) + inmode |= kNtEnableVirtualTerminalInput; + } } if (!(tio->c_iflag & ICRNL)) { __ttymagic |= kFdTtyNoCr2Nl; @@ -220,13 +225,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { if (!(tio->c_lflag & ISIG)) { __ttymagic |= kFdTtyNoIsigs; } - if (IsAtLeastWindows10()) { - inmode |= kNtEnableVirtualTerminalInput; - } __vintr = tio->c_cc[VINTR]; __vquit = tio->c_cc[VQUIT]; if ((tio->c_lflag & ISIG) && // tio->c_cc[VINTR] == CTRL('C')) { + // allows ctrl-c to be delivered asynchronously via win32 inmode |= kNtEnableProcessedInput; } ok = SetConsoleMode(hInput, inmode); diff --git a/libc/calls/winstdin1.c b/libc/calls/winstdin1.c index 9b91c442d..c88a1b875 100644 --- a/libc/calls/winstdin1.c +++ b/libc/calls/winstdin1.c @@ -20,7 +20,9 @@ #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/strace.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -30,6 +32,8 @@ #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" +#include "libc/thread/tls.h" +#include "libc/thread/tls2.internal.h" /** * @fileoverview makes windows stdin handle capable of being poll'd @@ -56,11 +60,24 @@ __msabi extern typeof(CreateFile) *const __imp_CreateFileW; __msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW; __msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW; __msabi extern typeof(CreateThread) *const __imp_CreateThread; +__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; __msabi extern typeof(ReadFile) *const __imp_ReadFile; __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; __msabi extern typeof(WriteFile) *const __imp_WriteFile; +static unsigned long StrLen(const char *s) { + unsigned long n = 0; + while (*s++) ++n; + return n; +} + +static void Log(const char *s) { +#if 0 + __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); +#endif +} + __msabi static dontasan dontubsan dontinstrument textwindows uint32_t WinStdinThread(void *lpParameter) { char buf[4096]; @@ -75,19 +92,19 @@ WinStdinThread(void *lpParameter) { __imp_CloseHandle(g_fds.stdin.inisem); // relay stdin to process - NTTRACE(" activated"); + Log(" activated\n"); for (;;) { if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) { - NTTRACE(" read failed"); + Log(" read failed\n"); goto Finish; } if (!got) { - NTTRACE(" end of file"); + Log(" end of file\n"); goto Finish; } for (i = 0; i < got; i += wrote) { if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { - NTTRACE(" failed to write to pipe"); + Log(" failed to write to pipe\n"); goto Finish; } } @@ -106,13 +123,13 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { if (!SupportsWindows()) return; hStdin = __imp_GetStdHandle(kNtStdInputHandle); if (hStdin == kNtInvalidHandleValue) { - NTTRACE(" GetStdHandle failed"); + Log(" GetStdHandle failed\n"); return; } // create non-inherited semaphore with initial value of 0 hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); if (!hSemaphore) { - NTTRACE(" CreateSemaphore failed"); + Log(" CreateSemaphore failed\n"); return; } __create_pipe_name(pipename); @@ -120,18 +137,18 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0); if (hReader == kNtInvalidHandleValue) { - NTTRACE(" CreateNamedPipe failed"); + Log(" CreateNamedPipe failed\n"); return; } hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, kNtFileFlagOverlapped, 0); if (hWriter == kNtInvalidHandleValue) { - NTTRACE(" CreateFile failed"); + Log(" CreateFile failed\n"); return; } hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); if (!hThread) { - NTTRACE(" CreateThread failed"); + Log(" CreateThread failed\n"); return; } g_fds.stdin.handle = hStdin; diff --git a/libc/runtime/clone-nt.S b/libc/calls/winthreadlaunch.S similarity index 96% rename from libc/runtime/clone-nt.S rename to libc/calls/winthreadlaunch.S index 6bcee3c23..b68b8386f 100644 --- a/libc/runtime/clone-nt.S +++ b/libc/calls/winthreadlaunch.S @@ -25,11 +25,11 @@ // This function switches us over, so that we can start using the // runtime facilities. // -// @param %rdi is arg -// @param %rsi is tid +// @param %rdi is arg1 +// @param %rsi is arg2 // @param %rdx is func // @param %rcx is stack -// @return %rax is exit code +// @return %rax is res // @see clone() WinThreadLaunch: push %rbx diff --git a/libc/intrin/aarch64/memcmp.S b/libc/intrin/aarch64/memcmp.S deleted file mode 100644 index de2ef3e8a..000000000 --- a/libc/intrin/aarch64/memcmp.S +++ /dev/null @@ -1,218 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╚──────────────────────────────────────────────────────────────────────────────╝ -│ │ -│ Optimized Routines │ -│ Copyright (c) 1999-2022, Arm Limited. │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ The above copyright notice and this permission notice shall be │ -│ included in all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ -│ │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/aarch64/asmdefs.internal.h" - -#define __memcmp_aarch64 memcmp - -.ident "\n\n\ -Optimized Routines (MIT License)\n\ -Copyright 2022 ARM Limited\n" -.include "libc/disclaimer.inc" - -/* Assumptions: - * - * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. - */ - -#define src1 x0 -#define src2 x1 -#define limit x2 -#define result w0 - -#define data1 x3 -#define data1w w3 -#define data2 x4 -#define data2w w4 -#define data3 x5 -#define data3w w5 -#define data4 x6 -#define data4w w6 -#define tmp x6 -#define src1end x7 -#define src2end x8 - - -ENTRY (__memcmp_aarch64) - PTR_ARG (0) - PTR_ARG (1) - SIZE_ARG (2) - - cmp limit, 16 - b.lo L(less16) - ldp data1, data3, [src1] - ldp data2, data4, [src2] - ccmp data1, data2, 0, ne - ccmp data3, data4, 0, eq - b.ne L(return2) - - add src1end, src1, limit - add src2end, src2, limit - cmp limit, 32 - b.ls L(last_bytes) - cmp limit, 160 - b.hs L(loop_align) - sub limit, limit, 32 - - .p2align 4 -L(loop32): - ldp data1, data3, [src1, 16] - ldp data2, data4, [src2, 16] - cmp data1, data2 - ccmp data3, data4, 0, eq - b.ne L(return2) - cmp limit, 16 - b.ls L(last_bytes) - - ldp data1, data3, [src1, 32] - ldp data2, data4, [src2, 32] - cmp data1, data2 - ccmp data3, data4, 0, eq - b.ne L(return2) - add src1, src1, 32 - add src2, src2, 32 -L(last64): - subs limit, limit, 32 - b.hi L(loop32) - - /* Compare last 1-16 bytes using unaligned access. */ -L(last_bytes): - ldp data1, data3, [src1end, -16] - ldp data2, data4, [src2end, -16] -L(return2): - cmp data1, data2 - csel data1, data1, data3, ne - csel data2, data2, data4, ne - - /* Compare data bytes and set return value to 0, -1 or 1. */ -L(return): -#ifndef __AARCH64EB__ - rev data1, data1 - rev data2, data2 -#endif - cmp data1, data2 - cset result, ne - cneg result, result, lo - ret - - .p2align 4 -L(less16): - add src1end, src1, limit - add src2end, src2, limit - tbz limit, 3, L(less8) - ldr data1, [src1] - ldr data2, [src2] - ldr data3, [src1end, -8] - ldr data4, [src2end, -8] - b L(return2) - - .p2align 4 -L(less8): - tbz limit, 2, L(less4) - ldr data1w, [src1] - ldr data2w, [src2] - ldr data3w, [src1end, -4] - ldr data4w, [src2end, -4] - b L(return2) - -L(less4): - tbz limit, 1, L(less2) - ldrh data1w, [src1] - ldrh data2w, [src2] - cmp data1w, data2w - b.ne L(return) -L(less2): - mov result, 0 - tbz limit, 0, L(return_zero) - ldrb data1w, [src1end, -1] - ldrb data2w, [src2end, -1] - sub result, data1w, data2w -L(return_zero): - ret - -L(loop_align): - ldp data1, data3, [src1, 16] - ldp data2, data4, [src2, 16] - cmp data1, data2 - ccmp data3, data4, 0, eq - b.ne L(return2) - - /* Align src2 and adjust src1, src2 and limit. */ - and tmp, src2, 15 - sub tmp, tmp, 16 - sub src2, src2, tmp - add limit, limit, tmp - sub src1, src1, tmp - sub limit, limit, 64 + 16 - - .p2align 4 -L(loop64): - ldr q0, [src1, 16] - ldr q1, [src2, 16] - subs limit, limit, 64 - ldr q2, [src1, 32] - ldr q3, [src2, 32] - eor v0.16b, v0.16b, v1.16b - eor v1.16b, v2.16b, v3.16b - ldr q2, [src1, 48] - ldr q3, [src2, 48] - umaxp v0.16b, v0.16b, v1.16b - ldr q4, [src1, 64]! - ldr q5, [src2, 64]! - eor v1.16b, v2.16b, v3.16b - eor v2.16b, v4.16b, v5.16b - umaxp v1.16b, v1.16b, v2.16b - umaxp v0.16b, v0.16b, v1.16b - umaxp v0.16b, v0.16b, v0.16b - fmov tmp, d0 - ccmp tmp, 0, 0, hi - b.eq L(loop64) - - /* If equal, process last 1-64 bytes using scalar loop. */ - add limit, limit, 64 + 16 - cbz tmp, L(last64) - - /* Determine the 8-byte aligned offset of the first difference. */ -#ifdef __AARCH64EB__ - rev16 tmp, tmp -#endif - rev tmp, tmp - clz tmp, tmp - bic tmp, tmp, 7 - sub tmp, tmp, 48 - ldr data1, [src1, tmp] - ldr data2, [src2, tmp] -#ifndef __AARCH64EB__ - rev data1, data1 - rev data2, data2 -#endif - mov result, 1 - cmp data1, data2 - cneg result, result, lo - ret - -END (__memcmp_aarch64) diff --git a/libc/intrin/exit.c b/libc/intrin/exit.c index 6acffe0c7..df241b7aa 100644 --- a/libc/intrin/exit.c +++ b/libc/intrin/exit.c @@ -85,10 +85,6 @@ wontreturn void _Exit(int exitcode) { #endif } else if (IsWindows()) { uint32_t waitstatus; - // Restoring the CMD.EXE program to its original state is critical. - if (_weaken(__restore_console_win32)) { - _weaken(__restore_console_win32)(); - } // What Microsoft calls an exit code, POSIX calls a status code. See // also the WEXITSTATUS() and WIFEXITED() macros that POSIX defines. waitstatus = exitcode; diff --git a/libc/intrin/memcmp.c b/libc/intrin/memcmp.c index 8e4c63a05..8688a2527 100644 --- a/libc/intrin/memcmp.c +++ b/libc/intrin/memcmp.c @@ -20,7 +20,6 @@ #include "libc/intrin/likely.h" #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" -#ifndef __aarch64__ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); @@ -83,5 +82,3 @@ int memcmp(const void *a, const void *b, size_t n) { } return 0; } - -#endif /* __aarch64__ */ diff --git a/libc/intrin/pthreadlist.c b/libc/intrin/pthreadlist.c new file mode 100644 index 000000000..0b5eb4813 --- /dev/null +++ b/libc/intrin/pthreadlist.c @@ -0,0 +1,22 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/dll.h" +#include "libc/thread/posixthread.internal.h" + +struct Dll *_pthread_list; diff --git a/libc/intrin/pthreadlock.c b/libc/intrin/pthreadlock.c new file mode 100644 index 000000000..f795764a4 --- /dev/null +++ b/libc/intrin/pthreadlock.c @@ -0,0 +1,22 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +pthread_spinlock_t _pthread_lock; diff --git a/libc/calls/realpath.c b/libc/mem/realpath.c similarity index 85% rename from libc/calls/realpath.c rename to libc/mem/realpath.c index 912d07f48..8579d025b 100644 --- a/libc/calls/realpath.c +++ b/libc/mem/realpath.c @@ -30,8 +30,6 @@ #include "libc/errno.h" #include "libc/intrin/bits.h" #include "libc/intrin/safemacros.internal.h" -#include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" #include "libc/mem/mem.h" @@ -44,7 +42,7 @@ asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ Copyright 2005-2014 Rich Felker, et. al.\""); asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ +// clang-format off static inline bool IsSlash(char c) { @@ -60,7 +58,7 @@ static size_t GetSlashLen(const char *s) static char *ResolvePath(char *d, const char *s, size_t n) { - if (d || (_weaken(malloc) && (d = _weaken(malloc)(n+1)))) { + if (d || (d = malloc(n+1))) { return memmove(d, s, n+1); } else { enomem(); @@ -71,11 +69,26 @@ static char *ResolvePath(char *d, const char *s, size_t n) /** * Returns absolute pathname. * - * This function removes `/./` and `/../` components. IF the path is a - * symbolic link then it's resolved. + * This function removes `/./` and `/../` components. If any individual + * path component is a symbolic link, then it'll be resolved. Any slash + * characters that repeat (e.g. `//`) will collapse into one (i.e. `/`) * - * @param resolved needs PATH_MAX bytes or NULL to use malloc() - * @return resolved or NULL w/ errno + * This implementation is consistent with glibc, in that `"//"` becomes + * `"/"` unlike Musl Libc, which considers that special (not sure why?) + * This is the only change Cosmopolitan Libc made vs. Musl's realpath() + * aside from also being permissive about backslashes, to help Windows. + * + * @param filename is the path that needs to be resolved + * @param resolved needs PATH_MAX bytes, or NULL to use malloc() + * @return resolved path, or NULL w/ errno + * @raise EINVAL if `filename` is NULL + * @raise ENOENT if `filename` is an empty string + * @raise ENOMEM if `resolved` is NULL and malloc() failed + * @raise ENOENT if `filename` didn't exist + * @raise ENOTDIR if directory component existed that's not a directory + * @raise ENOTDIR if base component ends with slash and is not a dir + * @raise ENAMETOOLONG if filename resolution exceeded `PATH_MAX` + * @raise ELOOP if too many symlinks were encountered */ char *realpath(const char *filename, char *resolved) { @@ -84,8 +97,6 @@ char *realpath(const char *filename, char *resolved) size_t k, p, q, l, l0, cnt=0, nup=0; char output[PATH_MAX], stack[PATH_MAX+1], *z; - /* STRACE("realpath(%#s, %#s)", filename, resolved); */ - if (!filename) { einval(); return 0; @@ -118,9 +129,6 @@ restart: q=0; output[q++] = '/'; p++; - /* Initial // is special. */ - if (IsSlash(stack[p]) && !IsSlash(stack[p+1])) - output[q++] = '/'; continue; } diff --git a/libc/nt/enum/context.h b/libc/nt/enum/context.h new file mode 100644 index 000000000..38d5d3583 --- /dev/null +++ b/libc/nt/enum/context.h @@ -0,0 +1,21 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ + +#define kNtContextAmd64 0x00100000 + +#define kNtContextControl (kNtContextAmd64 | 0x00000001) +#define kNtContextInteger (kNtContextAmd64 | 0x00000002) +#define kNtContextSegments (kNtContextAmd64 | 0x00000004) +#define kNtContextFloatingPoint (kNtContextAmd64 | 0x00000008) +#define kNtContextDebugRegisters (kNtContextAmd64 | 0x00000010) + +#define kNtContextFull \ + (kNtContextControl | kNtContextInteger | kNtContextFloatingPoint) + +#define kNtContextAll \ + (kNtContextControl | kNtContextInteger | kNtContextSegments | \ + kNtContextFloatingPoint | kNtContextDebugRegisters) + +#define kNtContextXstate (kNtContextAmd64 | 0x00000040) + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ */ diff --git a/libc/nt/kernel32/GetThreadContext.S b/libc/nt/kernel32/GetThreadContext.S new file mode 100644 index 000000000..ce82f481f --- /dev/null +++ b/libc/nt/kernel32/GetThreadContext.S @@ -0,0 +1,18 @@ +#include "libc/nt/codegen.h" +.imp kernel32,__imp_GetThreadContext,GetThreadContext + + .text.windows + .ftrace1 +GetThreadContext: + .ftrace2 +#ifdef __x86_64__ + push %rbp + mov %rsp,%rbp + mov __imp_GetThreadContext(%rip),%rax + jmp __sysv2nt +#elif defined(__aarch64__) + mov x0,#0 + ret +#endif + .endfn GetThreadContext,globl + .previous diff --git a/libc/nt/kernel32/InitializeContext.S b/libc/nt/kernel32/InitializeContext.S new file mode 100644 index 000000000..9572b9c9b --- /dev/null +++ b/libc/nt/kernel32/InitializeContext.S @@ -0,0 +1,18 @@ +#include "libc/nt/codegen.h" +.imp kernel32,__imp_InitializeContext,InitializeContext + + .text.windows + .ftrace1 +InitializeContext: + .ftrace2 +#ifdef __x86_64__ + push %rbp + mov %rsp,%rbp + mov __imp_InitializeContext(%rip),%rax + jmp __sysv2nt +#elif defined(__aarch64__) + mov x0,#0 + ret +#endif + .endfn InitializeContext,globl + .previous diff --git a/libc/nt/kernel32/ResumeThread.S b/libc/nt/kernel32/ResumeThread.S new file mode 100644 index 000000000..1f8a3304a --- /dev/null +++ b/libc/nt/kernel32/ResumeThread.S @@ -0,0 +1,20 @@ +#include "libc/nt/codegen.h" +.imp kernel32,__imp_ResumeThread,ResumeThread + + .text.windows + .ftrace1 +ResumeThread: + .ftrace2 +#ifdef __x86_64__ + push %rbp + mov %rsp,%rbp + mov %rdi,%rcx + sub $32,%rsp + call *__imp_ResumeThread(%rip) + leave +#elif defined(__aarch64__) + mov x0,#0 +#endif + ret + .endfn ResumeThread,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 920060e34..9dd105b9d 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -11,9 +11,9 @@ # Name Actual DLL Arity imp '' CloseHandle kernel32 1 imp '' CreateDirectoryW kernel32 2 +imp '' CreateFileA kernel32 7 imp '' CreateFileMappingNumaW kernel32 7 imp '' CreateFileMappingW kernel32 6 -imp '' CreateFileA kernel32 7 imp '' CreateFileW kernel32 7 imp '' CreateNamedPipeW kernel32 8 imp '' CreatePipe kernel32 4 @@ -162,6 +162,7 @@ imp 'GetSystemTimePreciseAsFileTime' GetSystemTimePreciseAsFileTime kernel3 imp 'GetSystemTimes' GetSystemTimes kernel32 3 imp 'GetTempPath' GetTempPathW kernel32 2 imp 'GetTempPathA' GetTempPathA kernel32 2 +imp 'GetThreadContext' GetThreadContext kernel32 2 imp 'GetThreadIOPendingFlag' GetThreadIOPendingFlag kernel32 2 imp 'GetThreadId' GetThreadId kernel32 1 imp 'GetThreadPriority' GetThreadPriority kernel32 1 @@ -182,6 +183,7 @@ imp 'HeapCreate' HeapCreate kernel32 3 imp 'HeapDestroy' HeapDestroy kernel32 1 imp 'HeapFree' HeapFree kernel32 3 imp 'HeapReAlloc' HeapReAlloc kernel32 4 +imp 'InitializeContext' InitializeContext kernel32 4 # Windows 7+ imp 'InitializeCriticalSection' InitializeCriticalSection kernel32 1 imp 'InitializeCriticalSectionAndSpinCount' InitializeCriticalSectionAndSpinCount kernel32 2 imp 'InitializeProcThreadAttributeList' InitializeProcThreadAttributeList kernel32 4 @@ -223,6 +225,7 @@ imp 'ReleaseSemaphore' ReleaseSemaphore kernel32 3 imp 'RemoveVectoredContinueHandler' RemoveVectoredContinueHandler kernel32 1 imp 'RemoveVectoredExceptionHandler' RemoveVectoredExceptionHandler kernel32 1 imp 'ResetEvent' ResetEvent kernel32 1 +imp 'ResumeThread' ResumeThread kernel32 1 imp 'SetConsoleActiveScreenBuffer' SetConsoleActiveScreenBuffer kernel32 1 # TODO(jart): 6.2 and higher imp 'SetConsoleCP' SetConsoleCP kernel32 1 # TODO(jart): 6.2 and higher imp 'SetConsoleCtrlHandler' SetConsoleCtrlHandler kernel32 2 diff --git a/libc/nt/thread.h b/libc/nt/thread.h index 446c33e10..fe33e2c3b 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_NT_THREADS_H_ #define COSMOPOLITAN_LIBC_NT_THREADS_H_ +#include "libc/nt/struct/context.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/securityattributes.h" #include "libc/nt/thunk/msabi.h" @@ -60,6 +61,10 @@ bool32 TlsFree(uint32_t); bool32 TlsSetValue(uint32_t, void *); void *TlsGetValue(uint32_t); +uint32_t SuspendThread(int64_t hThread); +uint32_t ResumeThread(int64_t hThread); +bool32 GetThreadContext(int64_t hThread, struct NtContext *in_out_lpContext); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/thread.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 867ce8907..184705bfa 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -40,8 +40,6 @@ extern unsigned char __tls_mov_nt_rax[]; extern unsigned char __tls_add_nt_rax[]; -struct Dll *_pthread_list; -pthread_spinlock_t _pthread_lock; static struct PosixThread _pthread_main; _Alignas(TLS_ALIGNMENT) static char __static_tls[6016]; diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 3ea2c65ec..c50d12a90 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -68,7 +68,7 @@ __static_yoink("_check_sigchld"); extern int64_t __wincrashearly; -bool32 __onntconsoleevent_nt(uint32_t); +bool32 __onntconsoleevent(uint32_t); void sys_setitimer_nt_reset(void); void kmalloc_unlock(void); @@ -308,8 +308,8 @@ textwindows void WinMainForked(void) { } AddVectoredExceptionHandler(1, (void *)_weaken(__wincrash_nt)); } - if (_weaken(__onntconsoleevent_nt)) { - SetConsoleCtrlHandler(_weaken(__onntconsoleevent_nt), 1); + if (_weaken(__onntconsoleevent)) { + SetConsoleCtrlHandler(_weaken(__onntconsoleevent), 1); } // jump back into function below diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 0bfb2dd7e..d89cbb65e 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -53,6 +53,7 @@ #include "libc/runtime/stack.h" #include "libc/runtime/winargs.internal.h" #include "libc/sock/internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/prot.h" #ifdef __x86_64__ @@ -66,6 +67,7 @@ __msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode; __msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess; __msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId; __msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW; +__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; __msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx; __msabi extern typeof(SetConsoleCP) *const __imp_SetConsoleCP; @@ -73,7 +75,6 @@ __msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode; __msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP; __msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle; __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; -__msabi extern typeof(WriteFile) *const __imp_WriteFile; // clang-format on extern void cosmo(int, char **, char **, long (*)[2]) wontreturn; @@ -84,20 +85,6 @@ static const signed char kNtStdio[3] = { (signed char)kNtStdErrorHandle, }; -static const short kConsoleModes[3] = { - kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput | - kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags | - kNtEnableAutoPosition | kNtEnableInsertMode | - kNtEnableVirtualTerminalInput, - kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | - kNtEnableVirtualTerminalProcessing, - kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | - kNtEnableVirtualTerminalProcessing, -}; - -static uint32_t __init_pid; -static uint32_t __console_mode[3]; - forceinline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } @@ -108,16 +95,6 @@ __msabi long __oops_win32(void) { return 0; } -// called by _exit to undo our config changes to cmd.exe -// it must never ever be called from forked subprocesses -void __restore_console_win32(void) { - if (__imp_GetCurrentProcessId() == __init_pid) { - for (int i = 0; i < 3; ++i) { - __imp_SetConsoleMode(__imp_GetStdHandle(kNtStdio[i]), __console_mode[i]); - } - } -} - // https://nullprogram.com/blog/2022/02/18/ __msabi static inline char16_t *MyCommandLine(void) { void *cmd; @@ -128,6 +105,15 @@ __msabi static inline char16_t *MyCommandLine(void) { return cmd; } +// returns true if utf-8 path is a win32-style path that exists +__msabi static textwindows bool32 WinFileExists(const char *path) { + uint16_t path16[PATH_MAX]; + size_t z = ARRAYLEN(path16); + size_t n = tprecode8to16(path16, z, path).ax; + if (n >= z - 1) return false; + return __imp_GetFileAttributesW(path16) != -1u; +} + // this ensures close(1) won't accidentally close(2) for example __msabi static textwindows void DeduplicateStdioHandles(void) { for (long i = 0; i < 3; ++i) { @@ -144,27 +130,30 @@ __msabi static textwindows void DeduplicateStdioHandles(void) { } } -__msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { - size_t stacksize; - struct WinArgs *wa; - uintptr_t stackaddr; - __init_pid = __pid; +// main function of windows init process +// i.e. first process spawned that isn't forked +__msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) { __oldstack = (intptr_t)__builtin_frame_address(0); + + // make console into utf-8 ansi/xterm style tty if (NtGetPeb()->OSMajorVersion >= 10 && (intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui) { __imp_SetConsoleCP(kNtCpUtf8); __imp_SetConsoleOutputCP(kNtCpUtf8); - for (int i = 0; i < 3; ++i) { - int64_t hand = __imp_GetStdHandle(kNtStdio[i]); - __imp_GetConsoleMode(hand, __console_mode + i); - __imp_SetConsoleMode(hand, kConsoleModes[i]); + for (int i = 1; i <= 2; ++i) { + uint32_t m; + intptr_t h = __imp_GetStdHandle(kNtStdio[i]); + __imp_GetConsoleMode(h, &m); + __imp_SetConsoleMode(h, m | kNtEnableVirtualTerminalProcessing); } } + + // allocate memory for stack and argument block _Static_assert(sizeof(struct WinArgs) % FRAMESIZE == 0, ""); _mmi.p = _mmi.s; _mmi.n = ARRAYLEN(_mmi.s); - stackaddr = GetStaticStackAddr(0); - stacksize = GetStaticStackSize(); + uintptr_t stackaddr = GetStaticStackAddr(0); + size_t stacksize = GetStaticStackSize(); __imp_MapViewOfFileEx((_mmi.p[0].h = __imp_CreateFileMappingW( -1, &kNtIsInheritable, kNtPageExecuteReadwrite, stacksize >> 32, stacksize, NULL)), @@ -181,25 +170,47 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { _mmi.p[0].flags = 0x00000026; // stack+anonymous _mmi.p[0].size = stacksize; _mmi.i = 1; - wa = (struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs))); + struct WinArgs *wa = + (struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs))); + + // parse utf-16 command into utf-8 argv array in argument block int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock), wa->argv, ARRAYLEN(wa->argv)); - for (int i = 0; wa->argv[0][i]; ++i) { - if (wa->argv[0][i] == '\\') { - wa->argv[0][i] = '/'; + + // munge argv so dos paths become cosmo paths + for (int i = 0; wa->argv[i]; ++i) { + if (wa->argv[i][0] == '\\' && // + wa->argv[i][1] == '\\') { + // don't munge new technology style paths + continue; + } + if (!WinFileExists(wa->argv[i])) { + // don't munge if we're not certain it's a file + continue; + } + // use forward slashes + for (int j = 0; wa->argv[i][j]; ++j) { + if (wa->argv[i][j] == '\\') { + wa->argv[i][j] = '/'; + } + } + // turn c:/... into /c/... + if (IsAlpha(wa->argv[i][0]) && // + wa->argv[i][1] == ':' && // + wa->argv[i][2] == '/') { + wa->argv[i][1] = wa->argv[i][0]; + wa->argv[i][0] = '/'; } } - if (IsAlpha(wa->argv[0][0]) && // - wa->argv[0][1] == ':' && // - wa->argv[0][2] == '/') { - wa->argv[0][1] = wa->argv[0][0]; - wa->argv[0][0] = '/'; - } + + // translate utf-16 win32 environment into utf-8 environment variables char16_t *env16 = __imp_GetEnvironmentStringsW(); GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp, ARRAYLEN(wa->envp) - 1); __imp_FreeEnvironmentStringsW(env16); __envp = &wa->envp[0]; + + // handover control to cosmopolitan runtime _jmpstack((char *)(stackaddr + (stacksize - sizeof(struct WinArgs))), cosmo, count, wa->argv, wa->envp, wa->auxv); } @@ -227,7 +238,7 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, if (_weaken(WinMainForked)) { _weaken(WinMainForked)(); } - WinMainNew(cmdline); + WinInit(cmdline); } #endif /* __x86_64__ */ diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index 439c9b6ae..d8b5bc584 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -49,6 +49,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped, // our i/o operation never happened because it failed return __winsockerr(); } +TryAgain: // our i/o operation is in flight and it needs to block abort_errno = EAGAIN; if (fd->flags & O_NONBLOCK) { @@ -87,10 +88,15 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped, // we wait for win32 to acknowledge that it's done using that memory. if (WSAGetOverlappedResult(fd->handle, overlapped, &got, true, flags)) { return got; - } else if (WSAGetLastError() == kNtErrorOperationAborted) { - errno = abort_errno; - return -1; - } else { - return -1; } + switch (WSAGetLastError()) { + case kNtErrorIoIncomplete: + goto TryAgain; + case kNtErrorOperationAborted: + errno = abort_errno; + break; + default: + break; + } + return -1; } diff --git a/libc/str/str.mk b/libc/str/str.mk index 07b53ff72..65aee1c11 100644 --- a/libc/str/str.mk +++ b/libc/str/str.mk @@ -94,6 +94,12 @@ o/$(MODE)/libc/fmt/strsignal.greg.o: private \ -ffreestanding \ $(NO_MAGIC) +# we can't use sanitizers because: +# WinMain calls this +o/$(MODE)/libc/str/tprecode8to16.o: private \ + COPTS += \ + -fno-sanitize=all + o/$(MODE)/libc/str/eastasianwidth.bin: \ libc/str/eastasianwidth.txt \ o/$(MODE)/tool/decode/mkwides.com diff --git a/libc/str/tprecode8to16.c b/libc/str/tprecode8to16.c index 13547c0cc..2100bf143 100644 --- a/libc/str/tprecode8to16.c +++ b/libc/str/tprecode8to16.c @@ -24,9 +24,9 @@ #include "libc/str/thompike.h" #include "libc/str/utf16.h" -/* 34x speedup for ascii */ -static inline dontasan axdx_t tprecode8to16_sse2(char16_t *dst, size_t dstsize, - const char *src, axdx_t r) { +// 34x speedup for ascii +static inline axdx_t tprecode8to16_sse2(char16_t *dst, size_t dstsize, + const char *src, axdx_t r) { uint8_t v1[16], v2[16], vz[16]; memset(vz, 0, 16); while (r.ax + 16 < dstsize) { @@ -62,9 +62,11 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { r.ax = 0; r.dx = 0; for (;;) { +#ifdef __x86_64__ if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { r = tprecode8to16_sse2(dst, dstsize, src, r); } +#endif x = src[r.dx++] & 0377; if (x >= 0300) { a = ThomPikeByte(x); diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index 3915895f0..2adbf57a1 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -47,6 +47,8 @@ TEST(readlink, enoent) { TEST(readlink, enotdir) { char buf[32]; ASSERT_SYS(0, 0, touch("o", 0644)); + ASSERT_SYS(ENOTDIR, -1, readlink("o/", buf, 32)); + ASSERT_SYS(ENOTDIR, -1, readlink("o/o/..", buf, 32)); ASSERT_SYS(ENOTDIR, -1, readlink("o/doesnotexist", buf, 32)); } @@ -95,7 +97,7 @@ TEST(readlinkat, frootloop) { } } -TEST(readlinkat, statReadsNameLength) { +TEST(readlinkat, statReadsNameLength_countsUtf8Bytes) { struct stat st; ASSERT_SYS(0, 0, symlink("froÒt", "froÒt")); ASSERT_SYS(0, 0, fstatat(AT_FDCWD, "froÒt", &st, AT_SYMLINK_NOFOLLOW)); diff --git a/test/libc/mem/realpath_test.c b/test/libc/mem/realpath_test.c new file mode 100644 index 000000000..a54b51472 --- /dev/null +++ b/test/libc/mem/realpath_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 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +char testlib_enable_tmp_setup_teardown; + +void SetUp(void) { + touch("conftest.a", 0644); + symlink("conftest.a", "conftest.l"); + mkdir("conftest.d", 0755); +} + +TEST(realpath, test1) { + char *name = gc(realpath("conftest.a", NULL)); + ASSERT_TRUE(name && *name == '/'); +} + +TEST(realpath, test2) { + ASSERT_EQ(NULL, gc(realpath("conftest.b/../conftest.a", NULL))); +} + +TEST(realpath, test3) { + char *name = gc(realpath("conftest.l/../conftest.a", NULL)); + if (IsWindows()) { + // WIN32 acts as a flat namespace, rather than linear inode crawl. + // GNU ./configure scripts consider this outcome to be acceptable. + ASSERT_NE(NULL, name); + } else { + // Every other OS FS is a UNIX inode crawl. + ASSERT_SYS(ENOTDIR, NULL, name); + } +} + +TEST(realpath, test4) { + ASSERT_SYS(ENOTDIR, NULL, gc(realpath("conftest.a/", NULL))); +} + +TEST(realpath, test5) { + char *name1 = gc(realpath(".", NULL)); + char *name2 = gc(realpath("conftest.d//./..", NULL)); + ASSERT_NE(NULL, name1); + ASSERT_NE(NULL, name2); + ASSERT_STREQ(name1, name2); +} + +TEST(realpath, test6) { + // musl libc fails this test + // comment say // is special but doesn't say why :/ + char *name = gc(realpath("//", NULL)); + ASSERT_NE(NULL, name); + EXPECT_STREQ("/", name); +} diff --git a/third_party/finger/finger.c b/third_party/finger/finger.c index b43715ca9..39c1e0dc8 100644 --- a/third_party/finger/finger.c +++ b/third_party/finger/finger.c @@ -45,6 +45,7 @@ #include "libc/runtime/utmp.h" #include "libc/sock/sock.h" #include "libc/sock/struct/sockaddr.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/time/time.h" diff --git a/tool/build/apelink.c b/tool/build/apelink.c index a03d6d418..a2fc18492 100644 --- a/tool/build/apelink.c +++ b/tool/build/apelink.c @@ -1458,9 +1458,10 @@ static char *SecondPass2(char *p, struct Input *in) { // the new file size. that's only possible if all the fat ape hdrs // we generate are able to fit inside the prologue. p = ALIGN(p, 8); + // TODO(jart): Figure out why not skewing corrupts pe import table in->we_must_skew_pe_vaspace = - ROUNDUP(p - prologue + in->size_of_pe_headers, - (int)in->pe->OptionalHeader.FileAlignment) > in->minload; + 1 || ROUNDUP(p - prologue + in->size_of_pe_headers, + (int)in->pe->OptionalHeader.FileAlignment) > in->minload; if (!in->we_must_skew_pe_vaspace) { in->pe_e_lfanew = p - prologue; in->pe_SizeOfHeaders = in->pe->OptionalHeader.SizeOfHeaders;