Make stdin pollable on Windows

You can now play Super Mario Bros in CMD.EXE using Cosmopolitan! This is
thanks to a new worker thread that's spawned on Windows whenever any one
of poll(), select(), or ioctl(FIONREAD) is linked.
This commit is contained in:
Justine Tunney 2023-08-13 22:42:25 -07:00
parent ef6387ee5e
commit 9c0821def7
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
15 changed files with 280 additions and 58 deletions

View file

@ -16,10 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/nt/process.h"
#include "libc/atomic.h"
#include "libc/intrin/atomic.h"
#include "libc/runtime/internal.h"
static textwindows char16_t *UintToChar16Array(char16_t p[21], uint64_t x) {
static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
char t;
size_t a, b, i = 0;
do {
@ -36,14 +37,15 @@ static textwindows char16_t *UintToChar16Array(char16_t p[21], uint64_t x) {
return p + i;
}
textwindows char16_t *CreatePipeName(char16_t *a) {
static long x;
// This function is called very early by WinMain().
dontasan textwindows char16_t *__create_pipe_name(char16_t *a) {
char16_t *p = a;
const char *q = "\\\\?\\pipe\\cosmo\\";
static atomic_uint x;
while (*q) *p++ = *q++;
p = UintToChar16Array(p, GetCurrentProcessId());
p = itoa16(p, __pid);
*p++ = '-';
p = UintToChar16Array(p, (x += 1));
p = itoa16(p, atomic_fetch_add(&x, 1));
*p = 0;
return a;
}

View file

@ -18,7 +18,9 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/termios.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -29,7 +31,10 @@
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/ipc.h"
#include "libc/nt/iphlpapi.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/ipadapteraddresses.h"
@ -48,6 +53,10 @@
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
__static_yoink("WinMainStdin");
#endif
/* Maximum number of unicast addresses handled for each interface */
#define MAX_UNICAST_ADDR 32
#define MAX_NAME_CLASH ((int)('z' - 'a')) /* Allow a..z */
@ -61,14 +70,10 @@ static struct HostAdapterInfoNode {
short flags;
} * __hostInfo;
static int ioctl_default(int fd, unsigned long request, ...) {
static int ioctl_default(int fd, unsigned long request, void *arg) {
int rc;
void *arg;
va_list va;
int64_t handle;
va_start(va, request);
arg = va_arg(va, void *);
va_end(va);
if (!IsWindows()) {
return sys_ioctl(fd, request, arg);
} else if (__isfdopen(fd)) {
@ -87,6 +92,36 @@ static int ioctl_default(int fd, unsigned long request, ...) {
}
}
static int ioctl_fionread(int fd, uint32_t *arg) {
int rc;
va_list va;
int64_t handle;
uint32_t avail;
if (!IsWindows()) {
return sys_ioctl(fd, FIONREAD, arg);
} else if (__isfdopen(fd)) {
handle = __resolve_stdin_handle(g_fds.p[fd].handle);
if (g_fds.p[fd].kind == kFdSocket) {
if ((rc = _weaken(__sys_ioctlsocket_nt)(handle, FIONREAD, arg)) != -1) {
return rc;
} else {
return _weaken(__winsockerr)();
}
} else if (GetFileType(handle) == kNtFileTypePipe) {
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {
*arg = avail;
return 0;
} else {
return __winerr();
}
} else {
return eopnotsupp();
}
} else {
return ebadf();
}
}
/* Frees all the nodes of the _hostInfo */
static textwindows void freeHostInfo(void) {
struct HostAdapterInfoNode *next, *node = __hostInfo;
@ -564,8 +599,7 @@ static int ioctl_siocgifflags(int fd, void *arg) {
* @param request can be any of:
*
* - `FIONREAD` takes an `int *` and returns how many bytes of input
* are available on a terminal or socket, waiting to be read. On
* Windows this currently won't work for console file descriptors.
* are available on a terminal or socket, waiting to be read.
*
* - `TIOCGWINSZ` populates `struct winsize *` with the dimensions
* of your teletypewriter. It's an alias for tcgetwinsize().
@ -635,8 +669,8 @@ int ioctl(int fd, unsigned long request, ...) {
va_start(va, request);
arg = va_arg(va, void *);
va_end(va);
if (request == FIONBIO) {
rc = ioctl_default(fd, request, arg);
if (request == FIONREAD) {
rc = ioctl_fionread(fd, arg);
} else if (request == TIOCGWINSZ) {
rc = tcgetwinsize(fd, arg);
} else if (request == TIOCSWINSZ) {

View file

@ -34,7 +34,7 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
int64_t hin, hout;
int reader, writer;
char16_t pipename[64];
CreatePipeName(pipename);
__create_pipe_name(pipename);
__fds_lock();
if ((reader = __reservefd_unlocked(-1)) == -1) {
__fds_unlock();

View file

@ -32,6 +32,8 @@
#include "libc/nt/runtime.h"
#include "libc/nt/struct/pollfd.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/nt/winsock.h"
#include "libc/sock/internal.h"
#include "libc/sock/struct/pollfd.h"
@ -43,6 +45,8 @@
#ifdef __x86_64__
__static_yoink("WinMainStdin");
/*
* Polls on the New Technology.
*
@ -92,7 +96,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
} else if (pn < ARRAYLEN(pipefds)) {
pipeindices[pn] = i;
pipefds[pn].handle = g_fds.p[fds[i].fd].handle;
pipefds[pn].handle = __resolve_stdin_handle(g_fds.p[fds[i].fd].handle);
pipefds[pn].events = 0;
pipefds[pn].revents = 0;
switch (g_fds.p[fds[i].fd].flags & O_ACCMODE) {

View file

@ -91,9 +91,11 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
bool32 ok;
uint32_t got;
int filetype;
int64_t handle;
int abort_errno = EAGAIN;
size = MIN(size, 0x7ffff000);
filetype = GetFileType(fd->handle);
handle = __resolve_stdin_handle(fd->handle);
filetype = GetFileType(handle);
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
struct NtOverlapped overlap = {0};
if (offset != -1) {
@ -103,18 +105,18 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) {
// the win32 manual says it's important to *not* put &got here
// since for overlapped i/o, we always use GetOverlappedResult
ok = ReadFile(fd->handle, data, size, 0, &overlap);
ok = ReadFile(handle, data, size, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
// i/o operation is in flight; blocking is unavoidable
// if we're in non-blocking mode, then immediately abort
// if an interrupt is pending, then abort before waiting
// otherwise wait for i/o periodically checking interrupts
if (fd->flags & O_NONBLOCK) {
sys_read_nt_abort(fd->handle, &overlap);
sys_read_nt_abort(handle, &overlap);
} else if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
Interrupted:
abort_errno = errno;
sys_read_nt_abort(fd->handle, &overlap);
sys_read_nt_abort(handle, &overlap);
} else {
for (;;) {
uint32_t i;
@ -134,7 +136,7 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
if (ok) {
// overlapped is allocated on stack, so it's important we wait
// for windows to acknowledge that it's done using that memory
ok = GetOverlappedResult(fd->handle, &overlap, &got, true);
ok = GetOverlappedResult(handle, &overlap, &got, true);
}
CloseHandle(overlap.hEvent);
} else {
@ -142,21 +144,21 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
}
} else if (offset == -1) {
// perform simple blocking file read
ok = ReadFile(fd->handle, data, size, &got, 0);
ok = ReadFile(handle, data, size, &got, 0);
} else {
// perform pread()-style file read at particular file offset
int64_t position;
// save file pointer which windows clobbers, even for overlapped i/o
if (!SetFilePointerEx(fd->handle, 0, &position, SEEK_CUR)) {
if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) {
return __winerr(); // fd probably isn't seekable?
}
struct NtOverlapped overlap = {0};
overlap.Pointer = (void *)(uintptr_t)offset;
ok = ReadFile(fd->handle, data, size, 0, &overlap);
ok = ReadFile(handle, data, size, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
if (ok) ok = GetOverlappedResult(fd->handle, &overlap, &got, true);
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
// restore file pointer which windows clobbers, even on error
unassert(SetFilePointerEx(fd->handle, position, 0, SEEK_SET));
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
}
if (ok) {
if (fd->ttymagic & kFdTtyMunging) {

View file

@ -28,12 +28,24 @@ struct Fd {
int64_t extra;
};
struct StdinRelay {
_Atomic(uint32_t) once;
int64_t inisem; /* semaphore to delay 1st read */
int64_t handle; /* should == g_fds.p[0].handle */
int64_t reader; /* ReadFile() use this instead */
int64_t writer; /* only used by WinStdinThread */
};
struct Fds {
_Atomic(int) f; /* lowest free slot */
size_t n;
struct Fd *p, *e;
struct StdinRelay stdin;
};
int64_t __resolve_stdin_handle(int64_t);
void WinMainStdin(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ */

View file

@ -7,7 +7,7 @@ bool isdirectory_nt(const char *);
bool isregularfile_nt(const char *);
bool issymlink_nt(const char *);
bool32 ntsetprivilege(int64_t, const char16_t *, uint32_t);
char16_t *CreatePipeName(char16_t *);
char16_t *__create_pipe_name(char16_t *);
int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]);
int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int);
int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]);

121
libc/calls/winstdin1.c Normal file
View file

@ -0,0 +1,121 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/strace.internal.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/ipc.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
__msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW;
__msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW;
__msabi extern typeof(CreateThread) *const __imp_CreateThread;
__msabi extern typeof(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;
__msabi static dontasan dontubsan dontinstrument textwindows uint32_t
WinStdinThread(void *lpParameter) {
char buf[4096];
uint32_t i, got, wrote;
// wait forever for semaphore to exceed 0
//
// this semaphore is unlocked the first time read or poll happens. we
// need it so the prog has time to call functions like SetConsoleMode
// before we begin performing i/o.
__imp_WaitForSingleObject(g_fds.stdin.inisem, -1u);
__imp_CloseHandle(g_fds.stdin.inisem);
// relay stdin to process
NTTRACE("<stdin> activated");
for (;;) {
if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) {
NTTRACE("<stdin> read failed");
goto Finish;
}
if (!got) {
NTTRACE("<stdin> end of file");
goto Finish;
}
for (i = 0; i < got; i += wrote) {
if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) {
NTTRACE("<stdin> failed to write to pipe");
goto Finish;
}
}
}
Finish:
__imp_CloseHandle(g_fds.stdin.writer);
return 0;
}
// this makes it possible for our read() implementation to periodically
// poll for signals while performing a blocking overlapped io operation
dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) {
uint32_t mode;
char16_t pipename[64];
int64_t hStdin, hWriter, hReader, hThread, hSemaphore;
hStdin = __imp_GetStdHandle(kNtStdInputHandle);
if (hStdin == kNtInvalidHandleValue) {
NTTRACE("<stdin> GetStdHandle failed");
return;
}
// create non-inherited semaphore with initial value of 0
hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0);
if (!hSemaphore) {
NTTRACE("<stdin> CreateSemaphore failed");
return;
}
__create_pipe_name(pipename);
hReader = __imp_CreateNamedPipeW(
pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped,
kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0);
if (hReader == kNtInvalidHandleValue) {
NTTRACE("<stdin> CreateNamedPipe failed");
return;
}
hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting,
kNtFileFlagOverlapped, 0);
if (hWriter == kNtInvalidHandleValue) {
NTTRACE("<stdin> CreateFile failed");
return;
}
hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0);
if (!hThread) {
NTTRACE("<stdin> CreateThread failed");
return;
}
__imp_CloseHandle(hThread);
g_fds.stdin.handle = hStdin;
g_fds.stdin.reader = hReader;
g_fds.stdin.writer = hWriter;
g_fds.stdin.inisem = hSemaphore;
}

32
libc/calls/winstdin2.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/intrin/atomic.h"
#include "libc/nt/synchronization.h"
textwindows int64_t __resolve_stdin_handle(int64_t handle) {
if (handle == g_fds.stdin.handle) {
if (!atomic_exchange(&g_fds.stdin.once, 1)) {
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
}
handle = g_fds.stdin.reader;
}
return handle;
}

View file

@ -31,9 +31,9 @@ __msabi extern typeof(CreateThread) *const __imp_CreateThread;
* @return thread handle, or 0 on failure
* @note this wrapper takes care of ABI, STRACE()
*/
textwindows int64_t CreateThread(
struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize,
NtThreadStartRoutine lpStartAddress, void *lpParameter,
textwindows int64_t
CreateThread(struct NtSecurityAttributes *lpThreadAttributes,
size_t dwStackSize, void *lpStartAddress, void *lpParameter,
uint32_t dwCreationFlags, uint32_t *opt_lpThreadId) {
int64_t hHandle;
hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,

View file

@ -30,10 +30,8 @@ COSMOPOLITAN_C_START_
cosmopolitan § new technology » threads
*/
typedef uint32_t (*NtThreadStartRoutine)(void *lpParameter);
int64_t CreateThread(struct NtSecurityAttributes *lpThreadAttributes,
size_t dwStackSize, NtThreadStartRoutine lpStartAddress,
size_t dwStackSize, void *lpStartAddress,
void *lpParameter, uint32_t dwCreationFlags,
uint32_t *opt_lpThreadId);

View file

@ -183,6 +183,10 @@ textwindows void WinMainForked(void) {
uint32_t i, varlen, oldprot, savepid;
long mapcount, mapcapacity, specialz;
struct StdinRelay stdin;
struct Fds *fds = __veil("r", &g_fds);
stdin = fds->stdin;
// check to see if the process was actually forked
// this variable should have the pipe handle numba
varlen = GetEnvironmentVariable(u"_FORK", fvar, ARRAYLEN(fvar));
@ -261,7 +265,7 @@ textwindows void WinMainForked(void) {
// rewrap the stdin named pipe hack
// since the handles closed on fork
struct Fds *fds = __veil("r", &g_fds);
fds->stdin = stdin;
fds->p[0].handle = GetStdHandle(kNtStdInputHandle);
fds->p[1].handle = GetStdHandle(kNtStdOutputHandle);
fds->p[2].handle = GetStdHandle(kNtStdErrorHandle);
@ -310,7 +314,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
tib = __tls_enabled ? __get_tls() : 0;
if (!setjmp(jb)) {
pid = untrackpid = __reservefd_unlocked(-1);
reader = CreateNamedPipe(CreatePipeName(pipename), kNtPipeAccessInbound,
reader = CreateNamedPipe(__create_pipe_name(pipename), kNtPipeAccessInbound,
kNtPipeTypeByte | kNtPipeReadmodeByte, 1, PIPE_BUF,
PIPE_BUF, 0, &kNtIsInheritable);
writer = CreateFile(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, 0, 0);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
@ -37,6 +38,7 @@
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/files.h"
#include "libc/nt/ipc.h"
#include "libc/nt/memory.h"
#include "libc/nt/pedef.internal.h"
#include "libc/nt/process.h"
@ -44,6 +46,8 @@
#include "libc/nt/signals.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/struct/teb.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
@ -54,6 +58,26 @@
#ifdef __x86_64__
/**
* @fileoverview makes windows stdin handle capable of being poll'd
*
* 1. On Windows, there's no way to check how many bytes of input are
* available from the cmd.exe console. The only thing you can do is a
* blocking read that can't be interrupted.
*
* 2. On Windows, it's up to the parent process whether or not the
* handles it passes us are capable of non-blocking overlapped i/o
* reads (which we need for busy polling to check for interrupts).
*
* So we solve this by creating a thread which just does naive reads on
* standard input, and then relays the data to the process via a named
* pipe, which we explicitly creaete with overlapped i/o enabled.
*
* This code runs very early during process initialization, at the
* beginning of WinMain(). This module is only activated if the app
* links any one of: poll(), select(), or ioctl(FIONREAD).
*/
// clang-format off
__msabi extern typeof(AddVectoredExceptionHandler) *const __imp_AddVectoredExceptionHandler;
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
@ -68,6 +92,7 @@ __msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
__msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW;
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
__msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx;
__msabi extern typeof(ReadFile) *const __imp_ReadFile;
__msabi extern typeof(SetConsoleCP) *const __imp_SetConsoleCP;
__msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
__msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP;
@ -147,22 +172,8 @@ __msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) {
__builtin_unreachable();
}
// this makes it possible for our read() implementation to periodically
// poll for signals while performing a blocking overlapped io operation
__msabi static textwindows void ReopenConsoleForOverlappedIo(void) {
uint32_t mode;
int64_t hOld, hNew;
hOld = __imp_GetStdHandle(kNtStdInputHandle);
if (__imp_GetConsoleMode(hOld, &mode)) {
hNew = __imp_CreateFileW(u"CONIN$", kNtGenericRead | kNtGenericWrite,
kNtFileShareRead | kNtFileShareWrite,
&kNtIsInheritable, kNtOpenExisting,
kNtFileFlagOverlapped, 0);
if (hNew != kNtInvalidHandleValue) {
__imp_SetStdHandle(kNtStdInputHandle, hNew);
__imp_CloseHandle(hOld);
}
}
__msabi static textwindows bool ProxyStdin(void) {
return true;
}
// this ensures close(1) won't accidentally close(2) for example
@ -196,8 +207,6 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
intptr_t stackaddr, allocaddr;
version = NtGetPeb()->OSMajorVersion;
__oldstack = (intptr_t)__builtin_frame_address(0);
ReopenConsoleForOverlappedIo();
DeduplicateStdioHandles();
if ((intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui && version >= 10) {
rc = __imp_SetConsoleCP(kNtCpUtf8);
NTTRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc);
@ -268,6 +277,10 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
os = _HOSTWINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */
kStartTsc = rdtsc();
__pid = __imp_GetCurrentProcessId();
DeduplicateStdioHandles();
if (_weaken(WinMainStdin)) {
_weaken(WinMainStdin)();
}
#if !IsTiny()
__wincrashearly =
__imp_AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash);

View file

@ -54,7 +54,7 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) {
return eopnotsupp();
}
CreatePipeName(pipename);
__create_pipe_name(pipename);
__fds_lock();
reader = __reservefd_unlocked(-1);
writer = __reservefd_unlocked(-1);

View file

@ -1625,7 +1625,7 @@ static char *FinishGeneratingDosHeader(char *p) {
p = WRITE16LE(p, 0); // 38
// terminate the shell quote started earlier in the ape magic. the big
// concern with shell script quoting is that binary content mimght get
// concern with shell script quoting, is that binary content might get
// generated in the dos stub which has an ascii value that is the same
// as the end of quote. using a longer terminator reduces it to a very
// low order of probability. tacking on an unpredictable deterministic