Get Redbean fork() working on the New Technology

Now that we have understandable system call tracing on Windows, this
change rewrites many of the polyfill internals for that platform, to
help things get closer to tip top shape. Support for complex forking
scenarios had been in a regressed state for quite some time. Now, it
works! Subsequent changes should be able to address the performance.
This commit is contained in:
Justine Tunney 2022-03-20 08:01:14 -07:00
parent efedef6e65
commit 0cb6b6ff4b
84 changed files with 1340 additions and 338 deletions

View file

@ -96,6 +96,7 @@ o//libc/calls/fcntl.o: \
OVERRIDE_CFLAGS += \
-Os
# must use alloca()
o/$(MODE)/libc/calls/execl.o \
o/$(MODE)/libc/calls/execle.o \
o/$(MODE)/libc/calls/execlp.o \

View file

@ -23,6 +23,7 @@
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/overlapped.h"
#include "libc/runtime/directmap.internal.h"
@ -33,7 +34,6 @@ textwindows noasan struct DirectMap sys_mmap_nt(void *addr, size_t size,
int prot, int flags,
int64_t handle, int64_t off) {
/* asan runtime depends on this function */
bool32 rc;
uint32_t got;
size_t i, upsize;
struct DirectMap dm;
@ -44,18 +44,12 @@ textwindows noasan struct DirectMap sys_mmap_nt(void *addr, size_t size,
* combination of flags, that'll cause Windows to actually do this!
*/
upsize = ROUNDUP(size, FRAMESIZE);
dm.maphandle = CreateFileMappingNuma(-1, &kNtIsInheritable,
kNtPageExecuteReadwrite, upsize >> 32,
upsize, NULL, kNtNumaNoPreferredNode);
STRACE(
"CreateFileMappingNuma(-1, kNtPageExecuteReadwrite, %'zu/%'zu) -> %p",
upsize, size, dm.maphandle);
if (dm.maphandle) {
dm.addr =
MapViewOfFileExNuma(dm.maphandle, kNtFileMapWrite | kNtFileMapExecute,
0, 0, upsize, addr, kNtNumaNoPreferredNode);
STRACE("MapViewOfFileExNuma(WX, %p) → addr:%p", addr, dm.addr);
if (dm.addr) {
if ((dm.maphandle = CreateFileMappingNuma(
-1, &kNtIsInheritable, kNtPageExecuteReadwrite, upsize >> 32,
upsize, NULL, kNtNumaNoPreferredNode))) {
if ((dm.addr = MapViewOfFileExNuma(
dm.maphandle, kNtFileMapWrite | kNtFileMapExecute, 0, 0, upsize,
addr, kNtNumaNoPreferredNode))) {
for (i = 0; i < size; i += got) {
got = 0;
op.Internal = 0;
@ -69,37 +63,27 @@ textwindows noasan struct DirectMap sys_mmap_nt(void *addr, size_t size,
if (i == size) {
return dm;
}
rc = UnmapViewOfFile(dm.addr);
STRACE("%s(addr:%p) → %hhhd% m", "UnmapViewOfFile", dm.maphandle, rc);
UnmapViewOfFile(dm.addr);
}
rc = CloseHandle(dm.maphandle);
STRACE("%s(%p) → %hhhd% m", "CloseHandle", dm.maphandle, rc);
CloseHandle(dm.maphandle);
}
} else {
dm.maphandle = CreateFileMappingNuma(
handle, &kNtIsInheritable,
(prot & PROT_WRITE) ? kNtPageExecuteReadwrite : kNtPageExecuteRead,
handle != -1 ? 0 : size >> 32, handle != -1 ? 0 : size, NULL,
kNtNumaNoPreferredNode);
STRACE("CreateFileMappingNuma(fhand:%ld, prot:%s, size:%'zu) → %p", handle,
(prot & PROT_WRITE) ? "XRW" : "XR", handle != -1 ? 0 : size);
if (dm.maphandle) {
dm.addr = MapViewOfFileExNuma(
dm.maphandle,
(prot & PROT_WRITE) ? kNtFileMapWrite | kNtFileMapExecute
: kNtFileMapRead | kNtFileMapExecute,
off >> 32, off, size, addr, kNtNumaNoPreferredNode);
STRACE("MapViewOfFileExNuma(prot:%s, off:%'ld, size:%'zu, addr:%p) → %p",
(prot & PROT_WRITE) ? "WX" : "RX", off, size, addr, dm.addr);
if (dm.addr) {
if ((dm.maphandle = CreateFileMappingNuma(
handle, &kNtIsInheritable,
(prot & PROT_WRITE) ? kNtPageExecuteReadwrite : kNtPageExecuteRead,
handle != -1 ? 0 : size >> 32, handle != -1 ? 0 : size, NULL,
kNtNumaNoPreferredNode))) {
if ((dm.addr = MapViewOfFileExNuma(
dm.maphandle,
(prot & PROT_WRITE) ? kNtFileMapWrite | kNtFileMapExecute
: kNtFileMapRead | kNtFileMapExecute,
off >> 32, off, size, addr, kNtNumaNoPreferredNode))) {
return dm;
} else {
rc = CloseHandle(dm.maphandle);
STRACE("%s(%p) → %d% m", "CloseHandle", dm.maphandle, rc);
}
CloseHandle(dm.maphandle);
}
}
dm.maphandle = kNtInvalidHandleValue;
dm.addr = (void *)(intptr_t)__winerr();
dm.addr = (void *)(intptr_t)-1;
return dm;
}

View file

@ -27,6 +27,7 @@
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
@ -54,5 +55,5 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
GetExitCodeProcess(procinfo.hProcess, &dwExitCode);
} while (dwExitCode == kNtStillActive);
CloseHandle(procinfo.hProcess);
ExitProcess(dwExitCode);
_Exit(dwExitCode);
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
@ -52,7 +51,7 @@ noasan noubsan privileged int mprotect(void *addr, size_t len, int prot) {
rc = -1;
}
} else {
if (__imp_VirtualProtect(addr, len, __prot2nt(prot, 0), &oldprot)) {
if (VirtualProtect(addr, len, __prot2nt(prot, 0), &oldprot)) {
rc = 0;
} else {
rc = __winerr();

View file

@ -84,7 +84,7 @@ textwindows int ntspawn(
(block =
MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
blocksize, NULL, kNtNumaNoPreferredNode))) {
if (mkntcmdline(block->cmdline, prog, argv + 1) != -1 &&
if (mkntcmdline(block->cmdline, prog, argv) != -1 &&
mkntenvblock(block->envvars, envp, extravar) != -1) {
if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
@ -95,10 +95,11 @@ textwindows int ntspawn(
} else {
__winerr();
}
STRACE("CreateProcess(%#hs, %#hs) → %d% m", prog16, block->cmdline, rc);
STRACE("CreateProcess(%#hs, %!#hs) → %d% m", prog16, block->cmdline, rc);
}
} else {
__winerr();
STRACE("ntspawn() alloc failed %m");
}
if (block) UnmapViewOfFile(block);
if (handle) CloseHandle(handle);

View file

@ -18,10 +18,12 @@
*/
#include "libc/bits/pushpop.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/nt/enum/ctrlevent.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
@ -31,16 +33,20 @@ textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
siginfo_t info;
switch (CtrlType) {
case kNtCtrlCEvent:
STRACE("kNtCtrlCEvent");
sig = pushpop(SIGINT);
break;
case kNtCtrlBreakEvent:
STRACE("kNtCtrlBreakEvent");
sig = pushpop(SIGQUIT);
break;
case kNtCtrlCloseEvent:
STRACE("kNtCtrlCloseEvent");
sig = pushpop(SIGHUP);
break;
case kNtCtrlLogoffEvent: // only received by services so hack hack hack
case kNtCtrlShutdownEvent: // only received by services so hack hack hack
STRACE("kNtCtrlLogoffEvent");
sig = pushpop(SIGALRM);
break;
default:
@ -48,7 +54,7 @@ textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
}
switch ((rva = __sighandrvas[sig])) {
case (uintptr_t)SIG_DFL:
ExitProcess(128 + sig);
_Exit(128 + sig);
case (uintptr_t)SIG_IGN:
return true;
default:

View file

@ -36,42 +36,82 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#define _O_APPEND 0x00000400 /* kNtFileAppendData */
#define _O_CREAT 0x00000040 /* kNtOpenAlways */
#define _O_EXCL 0x00000080 /* kNtCreateNew */
#define _O_TRUNC 0x00000200 /* kNtCreateAlways */
#define _O_DIRECTORY 0x00010000 /* kNtFileFlagBackupSemantics */
#define _O_TMPFILE 0x00410000 /* AttributeTemporary|FlagDeleteOnClose */
#define _O_DIRECT 0x00004000 /* kNtFileFlagNoBuffering */
#define _O_NDELAY 0x00000800 /* kNtFileFlagWriteThrough */
#define _O_RANDOM 0x80000000 /* kNtFileFlagRandomAccess */
#define _O_SEQUENTIAL 0x40000000 /* kNtFileFlagSequentialScan */
#define _O_COMPRESSED 0x20000000 /* kNtFileAttributeCompressed */
#define _O_INDEXED 0x10000000 /* !kNtFileAttributeNotContentIndexed */
#define _O_NONBLOCK 0x00000800
#define _O_CLOEXEC 0x00080000
static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
uint32_t flags, int32_t mode) {
uint32_t br;
int64_t handle;
char16_t path16[PATH_MAX];
uint32_t perm, share, disp, attr;
if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1;
if ((handle = CreateFile(
path16, flags & 0xf000000f, /* see consts.sh */
(flags & O_EXCL)
? kNtFileShareExclusive
: kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
&kNtIsInheritable,
(flags & O_CREAT) && (flags & O_EXCL) ? kNtCreateNew
: (flags & O_CREAT) && (flags & O_TRUNC) ? kNtCreateAlways
: (flags & O_CREAT) ? kNtOpenAlways
: (flags & O_TRUNC) ? kNtTruncateExisting
: kNtOpenExisting,
/* TODO(jart): Should we just always set overlapped? */
(/* note: content indexer demolishes unix-ey i/o performance */
kNtFileAttributeNotContentIndexed | kNtFileAttributeNormal |
(((flags & ((kNtFileFlagWriteThrough | kNtFileFlagOverlapped |
kNtFileFlagNoBuffering | kNtFileFlagRandomAccess) >>
8))
<< 8) |
(flags & (kNtFileFlagSequentialScan | kNtFileFlagDeleteOnClose |
kNtFileFlagBackupSemantics | kNtFileFlagPosixSemantics |
kNtFileAttributeTemporary)))),
0)) != -1) {
} else if (GetLastError() == kNtErrorFileExists &&
((flags & O_CREAT) &&
(flags & O_TRUNC))) { /* TODO(jart): What was this? */
handle = eisdir();
} else {
handle = __winerr();
switch (flags & O_ACCMODE) {
case O_RDONLY:
perm = kNtFileGenericRead | kNtGenericExecute;
break;
case O_WRONLY:
perm = kNtFileGenericWrite | kNtGenericExecute;
break;
case O_RDWR:
perm = kNtFileGenericRead | kNtFileGenericWrite | kNtGenericExecute;
break;
default:
unreachable;
}
if (flags & _O_EXCL) {
share = kNtFileShareExclusive;
} else {
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
}
if ((flags & _O_CREAT) && (flags & _O_EXCL)) {
disp = kNtCreateNew;
} else if ((flags & _O_CREAT) && (flags & _O_TRUNC)) {
disp = kNtCreateAlways;
} else if (flags & _O_CREAT) {
disp = kNtOpenAlways;
} else if (flags & _O_TRUNC) {
disp = kNtTruncateExisting;
} else {
disp = kNtOpenExisting;
}
if ((flags & _O_TMPFILE) == _O_TMPFILE) {
attr = kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
} else {
attr = kNtFileAttributeNormal;
if (flags & _O_DIRECTORY) attr |= kNtFileFlagBackupSemantics;
}
flags |= kNtFileFlagOverlapped;
if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed;
if (flags & _O_COMPRESSED) attr |= kNtFileAttributeCompressed;
if (flags & _O_SEQUENTIAL) attr |= kNtFileFlagSequentialScan;
if (flags & _O_RANDOM) attr |= kNtFileFlagRandomAccess;
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;
if (flags & _O_NDELAY) attr |= kNtFileFlagWriteThrough;
if ((handle = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr,
0)) != -1) {
} else if (GetLastError() == kNtErrorFileExists &&
((flags & _O_CREAT) &&
(flags & _O_TRUNC))) { /* TODO(jart): What was this? */
handle = eisdir();
}
STRACE("CreateFile() → %ld% m", handle);
return handle;
}

View file

@ -1,49 +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 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#define HAS(X, BITS) (((X) & (BITS)) == (BITS))
/**
* Converts System Five memory protection flags to Windows NT, Part 1.
* @see libc/sysv/consts.sh
*/
privileged uint32_t __prot2nt(int prot, int flags) {
return (HAS(prot, PROT_READ | PROT_WRITE | PROT_EXEC)
? (HAS(flags, MAP_SHARED) || HAS(flags, MAP_ANONYMOUS))
? kNtPageExecuteReadwrite
: kNtPageExecuteWritecopy
: HAS(prot, PROT_READ | PROT_WRITE)
? (HAS(flags, MAP_SHARED) || HAS(flags, MAP_ANONYMOUS))
? kNtPageReadwrite
: kNtPageReadwrite /* kNtPageWritecopy */
: HAS(prot, PROT_READ | PROT_EXEC)
? kNtPageExecuteRead
: HAS(prot, PROT_EXEC)
? kNtPageExecute
: HAS(prot, PROT_READ) ? kNtPageReadonly
: kNtPageNoaccess) |
((prot | flags) &
(kNtSecReserve | kNtSecCommit | kNtSecImage | kNtSecImageNoExecute |
kNtSecLargePages | kNtSecNocache | kNtSecWritecombine));
}

View file

@ -19,8 +19,10 @@
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sig.h"
/**
@ -32,6 +34,7 @@
*/
int raise(int sig) {
int event;
STRACE("raise(%d)", sig);
if (sig == SIGTRAP) {
DebugBreak();
return 0;
@ -50,6 +53,6 @@ int raise(int sig) {
return __winerr();
}
} else {
ExitProcess(128 + sig);
_Exit(128 + sig);
}
}

View file

@ -126,7 +126,6 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
}
CloseHandle(h);
} else {
STRACE("%s failed %m", "CreateFile(kNtFileFlagOpenReparsePoint)");
rc = sys_readlinkat_nt_error();
}
if (freeme && weaken(free)) {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/time/time.h"
@ -24,5 +25,8 @@
* @asyncsignalsafe
*/
int sleep(uint32_t seconds) {
return nanosleep(&(struct timespec){seconds, 0}, NULL);
int rc;
rc = nanosleep(&(struct timespec){seconds, 0}, NULL);
STRACE("sleep(%u) → %d% m", seconds, rc);
return rc;
}

View file

@ -1,46 +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 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/bits/weaken.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/sock/internal.h"
#include "libc/sysv/errfuns.h"
/**
* Return path for failed Win32 API calls.
*
* @return -1 w/ few exceptions
* @note this is a code-size saving device
*/
privileged int64_t __winerr(void) {
errno_t e;
if (IsWindows()) {
e = __imp_GetLastError();
if (weaken(__dos2errno)) {
e = weaken(__dos2errno)(e);
}
} else {
e = ENOSYS;
}
errno = e;
return -1;
}