cosmopolitan/libc/runtime/winmain.greg.c
Justine Tunney db33973e0a Get fork() working on Windows
This is done without using Microsoft's internal APIs. MAP_PRIVATE
mappings are copied to the subprocess via a pipe, since Microsoft
doesn't want us to have proper COW pages. MAP_SHARED mappings are
remapped without needing to do any copying. Global variables need
copying along with the stack and the whole heap of anonymous mem.
This actually improves the reliability of the redbean http server
although one shouldn't expect 10k+ connections on a home computer
that isn't running software built to serve like Linux or FreeBSD.
2020-11-13 03:14:39 -08:00

201 lines
8.5 KiB
C

/*-*- 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 │
│ │
│ This program is free software; you can redistribute it and/or modify │
│ it under the terms of the GNU General Public License as published by │
│ the Free Software Foundation; version 2 of the License. │
│ │
│ This program is distributed in the hope that it will be useful, but │
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
│ General Public License for more details. │
│ │
│ You should have received a copy of the GNU General Public License │
│ along with this program; if not, write to the Free Software │
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
│ 02110-1301 USA │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/bits.h"
#include "libc/bits/pushpop.h"
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/consolemodeflags.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/enum/loadlibrarysearch.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/files.h"
#include "libc/nt/memory.h"
#include "libc/nt/pedef.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/teb.h"
#include "libc/runtime/getdosenviron.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.h"
#include "libc/runtime/missioncritical.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/winmain.h"
#include "libc/sock/internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
struct WinArgs {
char *argv[512];
char *envp[512];
long auxv[1][2];
char argblock[ARG_MAX];
char envblock[ENV_MAX];
};
static struct CmdExe {
bool result;
struct OldNtConsole {
uint32_t codepage;
uint32_t mode;
int64_t handle;
} oldin, oldout;
} g_cmdexe;
struct WinMain g_winmain;
static textwindows void RestoreCmdExe(void) {
if (g_cmdexe.oldin.handle) {
SetConsoleCP(g_cmdexe.oldin.codepage);
SetConsoleMode(g_cmdexe.oldin.handle, g_cmdexe.oldin.mode);
}
if (g_cmdexe.oldout.handle) {
SetConsoleOutputCP(g_cmdexe.oldout.codepage);
SetConsoleMode(g_cmdexe.oldout.handle, g_cmdexe.oldout.mode);
}
}
static textwindows void SetTrueColor(void) {
SetEnvironmentVariable(u"TERM", u"xterm-truecolor");
}
static textwindows void NormalizeCmdExe(void) {
int64_t handle, hstdin, hstdout, hstderr;
if ((int)weakaddr("v_ntsubsystem") == kNtImageSubsystemWindowsCui &&
NtGetVersion() >= kNtVersionWindows10) {
atexit(RestoreCmdExe);
hstdin = GetStdHandle(pushpop(kNtStdInputHandle));
hstdout = GetStdHandle(pushpop(kNtStdOutputHandle));
hstderr = GetStdHandle(pushpop(kNtStdErrorHandle));
if (GetFileType((handle = hstdin)) == kNtFileTypeChar) {
SetTrueColor();
g_cmdexe.oldin.handle = handle;
g_cmdexe.oldin.codepage = GetConsoleCP();
SetConsoleCP(kNtCpUtf8);
GetConsoleMode(handle, &g_cmdexe.oldin.mode);
SetConsoleMode(handle, g_cmdexe.oldin.mode | kNtEnableProcessedInput |
kNtEnableEchoInput | kNtEnableLineInput |
kNtEnableWindowInput |
kNtEnableVirtualTerminalInput);
}
if (GetFileType((handle = hstdout)) == kNtFileTypeChar ||
GetFileType((handle = hstderr)) == kNtFileTypeChar) {
SetTrueColor();
g_cmdexe.oldout.handle = handle;
g_cmdexe.oldout.codepage = GetConsoleOutputCP();
SetConsoleOutputCP(kNtCpUtf8);
GetConsoleMode(handle, &g_cmdexe.oldout.mode);
SetConsoleMode(handle, g_cmdexe.oldout.mode | kNtEnableProcessedOutput |
kNtEnableWrapAtEolOutput |
(NtGetVersion() >= kNtVersionWindows10
? kNtEnableVirtualTerminalProcessing
: 0));
}
}
}
static textwindows char *AllocateMemory(void *addr, size_t size, int64_t *h) {
return MapViewOfFileExNuma(
(*h = CreateFileMappingNuma(-1, NULL, pushpop(kNtPageReadwrite), 0, size,
NULL, kNtNumaNoPreferredNode)),
kNtFileMapRead | kNtFileMapWrite, 0, 0, size, addr,
kNtNumaNoPreferredNode);
}
static textwindows noreturn void WinMainNew(int64_t hInstance,
int64_t hPrevInstance,
const char *lpCmdLine,
int nCmdShow) {
int64_t h;
size_t size;
int i, count;
uint64_t data;
struct WinArgs *wa;
const char16_t *env16;
g_winmain.nCmdShow = nCmdShow;
g_winmain.hInstance = hInstance;
g_winmain.hPrevInstance = hPrevInstance;
NormalizeCmdExe();
*(/*unconst*/ int *)&hostos = WINDOWS;
size = ROUNDUP(STACKSIZE + sizeof(struct WinArgs), FRAMESIZE);
data = 0x777000000000;
data = (intptr_t)AllocateMemory((char *)data, size, &_mmi.p[0].h);
_mmi.p[0].x = data >> 16;
_mmi.p[0].y = (data >> 16) + ((size >> 16) - 1);
_mmi.p[0].prot = PROT_READ | PROT_WRITE;
_mmi.p[0].flags = MAP_PRIVATE | MAP_ANONYMOUS;
_mmi.i = pushpop(1L);
wa = (struct WinArgs *)(data + size - sizeof(struct WinArgs));
count = GetDosArgv(GetCommandLine(), wa->argblock, ARG_MAX, wa->argv, 512);
for (i = 0; wa->argv[0][i]; ++i) {
if (wa->argv[0][i] == '\\') {
wa->argv[0][i] = '/';
}
}
env16 = GetEnvironmentStrings();
GetDosEnviron(env16, wa->envblock, ENV_MAX, wa->envp, 512);
FreeEnvironmentStrings(env16);
wa->auxv[0][0] = pushpop(0L);
wa->auxv[0][1] = pushpop(0L);
_jmpstack((char *)data + STACKSIZE, _executive, count, wa->argv, wa->envp,
wa->auxv);
}
/**
* Main function on Windows NT.
*
* The Cosmopolitan Runtime provides the following services, which aim
* to bring Windows NT behavior closer in harmony with System Five:
*
* 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10.
*
* 2. Command line arguments are passed as a blob of UTF-16 text. We
* chop them up into an char *argv[] UTF-8 data structure, in
* accordance with the DOS conventions for argument quoting.
*
* 3. Environment variables are passed to us as a sorted UTF-16 double
* NUL terminated list. We translate this to char ** using UTF-8.
*
* 4. Allocates new stack at a high address. NT likes to choose a
* stack address that's beneath the program image. We want to be
* able to assume that stack addresses are located at higher
* addresses than heap and program memory.
*
* 5. Windows users are afraid of "drive-by downloads" where someone
* might accidentally an evil DLL to their Downloads folder which
* then overrides the behavior of a legitimate EXE being run from
* the downloads folder. Since we don't even use dynamic linking,
* we've cargo culted some API calls, that may harden against it.
*
* 6. Finally, we need fork. Microsoft designed Windows to prevent us
* from having fork() so we pass pipe handles in an environment
* variable literally copy all the memory.
*
*/
textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
const char *lpCmdLine, int nCmdShow) {
SetDefaultDllDirectories(kNtLoadLibrarySearchSearchSystem32);
if (weaken(winsockinit)) weaken(winsockinit)();
if (weaken(WinMainForked)) weaken(WinMainForked)();
WinMainNew(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}