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!
This commit is contained in:
Justine Tunney 2023-09-06 22:39:36 -07:00
parent f9c9a323fe
commit 425c055116
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
40 changed files with 581 additions and 706 deletions

View file

@ -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);
}
}

View file

@ -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("<stdin>");

View file

@ -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");

View file

@ -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);

View file

@ -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 *);

View file

@ -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 \

View file

@ -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) {

View file

@ -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();

View file

@ -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"

View file

@ -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__ */

View file

@ -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

View file

@ -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 {

View file

@ -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);

View file

@ -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) {

View file

@ -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__ */

View file

@ -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);

View file

@ -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("<stdin> activated");
Log("<stdin> activated\n");
for (;;) {
if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) {
NTTRACE("<stdin> read failed");
Log("<stdin> read failed\n");
goto Finish;
}
if (!got) {
NTTRACE("<stdin> end of file");
Log("<stdin> 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("<stdin> failed to write to pipe");
Log("<stdin> 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("<stdin> GetStdHandle failed");
Log("<stdin> GetStdHandle failed\n");
return;
}
// create non-inherited semaphore with initial value of 0
hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0);
if (!hSemaphore) {
NTTRACE("<stdin> CreateSemaphore failed");
Log("<stdin> 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("<stdin> CreateNamedPipe failed");
Log("<stdin> CreateNamedPipe failed\n");
return;
}
hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting,
kNtFileFlagOverlapped, 0);
if (hWriter == kNtInvalidHandleValue) {
NTTRACE("<stdin> CreateFile failed");
Log("<stdin> CreateFile failed\n");
return;
}
hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0);
if (!hThread) {
NTTRACE("<stdin> CreateThread failed");
Log("<stdin> CreateThread failed\n");
return;
}
g_fds.stdin.handle = hStdin;

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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__ */

22
libc/intrin/pthreadlist.c Normal file
View file

@ -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;

22
libc/intrin/pthreadlock.c Normal file
View file

@ -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;

View file

@ -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;
}

21
libc/nt/enum/context.h Normal file
View file

@ -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_ */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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() */

View file

@ -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];

View file

@ -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

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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));

View file

@ -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);
}

View file

@ -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"

View file

@ -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;