Make improvements

- Every unit test now passes on Apple Silicon. The final piece of this
  puzzle was porting our POSIX threads cancelation support, since that
  works differently on ARM64 XNU vs. AMD64. Our semaphore support on
  Apple Silicon is also superior now compared to AMD64, thanks to the
  grand central dispatch library which lets *NSYNC locks go faster.

- The Cosmopolitan runtime is now more stable, particularly on Windows.
  To do this, thread local storage is mandatory at all runtime levels,
  and the innermost packages of the C library is no longer being built
  using ASAN. TLS is being bootstrapped with a 128-byte TIB during the
  process startup phase, and then later on the runtime re-allocates it
  either statically or dynamically to support code using _Thread_local.
  fork() and execve() now do a better job cooperating with threads. We
  can now check how much stack memory is left in the process or thread
  when functions like kprintf() / execve() etc. call alloca(), so that
  ENOMEM can be raised, reduce a buffer size, or just print a warning.

- POSIX signal emulation is now implemented the same way kernels do it
  with pthread_kill() and raise(). Any thread can interrupt any other
  thread, regardless of what it's doing. If it's blocked on read/write
  then the killer thread will cancel its i/o operation so that EINTR can
  be returned in the mark thread immediately. If it's doing a tight CPU
  bound operation, then that's also interrupted by the signal delivery.
  Signal delivery works now by suspending a thread and pushing context
  data structures onto its stack, and redirecting its execution to a
  trampoline function, which calls SetThreadContext(GetCurrentThread())
  when it's done.

- We're now doing a better job managing locks and handles. On NetBSD we
  now close semaphore file descriptors in forked children. Semaphores on
  Windows can now be canceled immediately, which means mutexes/condition
  variables will now go faster. Apple Silicon semaphores can be canceled
  too. We're now using Apple's pthread_yield() funciton. Apple _nocancel
  syscalls are now used on XNU when appropriate to ensure pthread_cancel
  requests aren't lost. The MbedTLS library has been updated to support
  POSIX thread cancelations. See tool/build/runitd.c for an example of
  how it can be used for production multi-threaded tls servers. Handles
  on Windows now leak less often across processes. All i/o operations on
  Windows are now overlapped, which means file pointers can no longer be
  inherited across dup() and fork() for the time being.

- We now spawn a thread on Windows to deliver SIGCHLD and wakeup wait4()
  which means, for example, that posix_spawn() now goes 3x faster. POSIX
  spawn is also now more correct. Like Musl, it's now able to report the
  failure code of execve() via a pipe although our approach favors using
  shared memory to do that on systems that have a true vfork() function.

- We now spawn a thread to deliver SIGALRM to threads when setitimer()
  is used. This enables the most precise wakeups the OS makes possible.

- The Cosmopolitan runtime now uses less memory. On NetBSD for example,
  it turned out the kernel would actually commit the PT_GNU_STACK size
  which caused RSS to be 6mb for every process. Now it's down to ~4kb.
  On Apple Silicon, we reduce the mandatory upstream thread size to the
  smallest possible size to reduce the memory overhead of Cosmo threads.
  The examples directory has a program called greenbean which can spawn
  a web server on Linux with 10,000 worker threads and have the memory
  usage of the process be ~77mb. The 1024 byte overhead of POSIX-style
  thread-local storage is now optional; it won't be allocated until the
  pthread_setspecific/getspecific functions are called. On Windows, the
  threads that get spawned which are internal to the libc implementation
  use reserve rather than commit memory, which shaves a few hundred kb.

- sigaltstack() is now supported on Windows, however it's currently not
  able to be used to handle stack overflows, since crash signals are
  still generated by WIN32. However the crash handler will still switch
  to the alt stack, which is helpful in environments with tiny threads.

- Test binaries are now smaller. Many of the mandatory dependencies of
  the test runner have been removed. This ensures many programs can do a
  better job only linking the the thing they're testing. This caused the
  test binaries for LIBC_FMT for example, to decrease from 200kb to 50kb

- long double is no longer used in the implementation details of libc,
  except in the APIs that define it. The old code that used long double
  for time (instead of struct timespec) has now been thoroughly removed.

- ShowCrashReports() is now much tinier in MODE=tiny. Instead of doing
  backtraces itself, it'll just print a command you can run on the shell
  using our new `cosmoaddr2line` program to view the backtrace.

- Crash report signal handling now works in a much better way. Instead
  of terminating the process, it now relies on SA_RESETHAND so that the
  default SIG_IGN behavior can terminate the process if necessary.

- Our pledge() functionality has now been fully ported to AARCH64 Linux.
This commit is contained in:
Justine Tunney 2023-09-18 20:44:45 -07:00
parent c4eb838516
commit ec480f5aa0
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
638 changed files with 7925 additions and 8282 deletions

View file

@ -1,225 +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 2022 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 "ape/sections.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/struct/context.h"
#include "libc/nt/thread.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
/**
* Returns true if signal is ignored by default.
*/
textwindows bool __sig_is_ignored(int sig) {
return sig == SIGURG || //
sig == SIGCONT || //
sig == SIGCHLD || //
sig == SIGWINCH;
}
/**
* Returns true if signal is so fatal it should dump core.
*/
textwindows bool __sig_is_core(int sig) {
return sig == SIGSYS || //
sig == SIGBUS || //
sig == SIGSEGV || //
sig == SIGQUIT || //
sig == SIGTRAP || //
sig == SIGXCPU || //
sig == SIGXFSZ;
}
static inline textwindows int __sig_is_masked(int sig) {
if (__tls_enabled) {
return __get_tls()->tib_sigmask & (1ull << (sig - 1));
} else {
return __sig.mask & (1ull << (sig - 1));
}
}
/**
* Delivers signal to callback.
*
* @return true if `EINTR` should be raised
*/
bool __sig_deliver(int sigops, int sig, int sic, ucontext_t *ctx) {
unsigned rva = __sighandrvas[sig];
unsigned flags = __sighandflags[sig];
// generate expensive data if needed
ucontext_t uc;
siginfo_t info;
siginfo_t *infop;
if (flags & SA_SIGINFO) {
__repstosb(&info, 0, sizeof(info));
info.si_signo = sig;
info.si_code = sic;
infop = &info;
if (!ctx) {
struct NtContext nc = {.ContextFlags = kNtContextAll};
__repstosb(&uc, 0, sizeof(uc));
GetThreadContext(GetCurrentThread(), &nc);
_ntcontext2linux(&uc, &nc);
ctx = &uc;
}
} else {
infop = 0;
}
// save the thread's signal mask
uint64_t oldmask;
if (__tls_enabled) {
oldmask = __get_tls()->tib_sigmask;
} else {
oldmask = __sig.mask;
}
if (ctx) {
ctx->uc_sigmask = (sigset_t){{oldmask}};
}
// mask the signal that's being handled whilst handling
if (!(flags & SA_NODEFER)) {
if (__tls_enabled) {
__get_tls()->tib_sigmask |= 1ull << (sig - 1);
} else {
__sig.mask |= 1ull << (sig - 1);
}
}
STRACE("delivering %G", sig);
((sigaction_f)(__executable_start + rva))(sig, infop, ctx);
if (ctx) {
oldmask = ctx->uc_sigmask.__bits[0];
}
if (__tls_enabled) {
__get_tls()->tib_sigmask = oldmask;
} else {
__sig.mask = oldmask;
}
if (flags & SA_RESETHAND) {
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
if (!(sigops & kSigOpRestartable)) {
return true; // always send EINTR for wait4(), poll(), etc.
} else if (flags & SA_RESTART) {
STRACE("restarting syscall on %G", sig);
return false; // resume syscall for read(), write(), etc.
} else {
return true; // default course is to raise EINTR
}
}
/**
* Handles signal.
* @return true if `EINTR` should be raised
*/
textwindows bool __sig_handle(int sigops, int sig, int sic, ucontext_t *ctx) {
if (__sighandrvas[sig] == (intptr_t)SIG_IGN ||
(__sighandrvas[sig] == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) {
return false;
}
if (__sig_is_masked(sig)) {
if (sigops & kSigOpUnmaskable) {
goto DefaultAction;
}
if (__tls_enabled) {
__get_tls()->tib_sigpending |= 1ull << (sig - 1);
} else {
__sig.pending |= 1ull << (sig - 1);
}
return false;
}
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
DefaultAction:
if (!__sig_is_ignored(sig)) {
uint32_t cmode;
intptr_t hStderr;
const char *signame;
char *end, sigbuf[21], output[123];
signame = strsignal_r(sig, sigbuf);
STRACE("terminating due to uncaught %s", signame);
if (__sig_is_core(sig)) {
hStderr = GetStdHandle(kNtStdErrorHandle);
if (GetConsoleMode(hStderr, &cmode)) {
end = stpcpy(output, signame);
end = stpcpy(end, " ");
end = stpcpy(
end,
DescribeBacktrace(
ctx ? (struct StackFrame *)ctx->uc_mcontext.BP
: (struct StackFrame *)__builtin_frame_address(0)));
end = stpcpy(end, "\n");
WriteFile(hStderr, output, end - output, 0, 0);
}
}
ExitProcess(sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
return false;
default:
return __sig_deliver(sigops, sig, sic, ctx);
}
}
static textwindows bool __sig_checkem(int sigops, uint64_t *pending) {
bool res = false;
for (int sig = 1; sig <= 64; ++sig) {
if (*pending & (1ull << (sig - 1))) {
*pending &= ~(1ull << (sig - 1));
res |= __sig_handle(sigops, sig, SI_KERNEL, 0);
}
}
return res;
}
/**
* Checks for unblocked signals and delivers them on New Technology.
* @return true if EINTR should be returned by caller
* @note called from main thread
* @threadsafe
*/
textwindows bool __sig_check(int sigops) {
bool res = false;
if (__tls_enabled) {
res |= __sig_checkem(sigops, &__get_tls()->tib_sigpending);
}
return res | __sig_checkem(sigops, &__sig.pending);
}
#endif /* __x86_64__ */

View file

@ -40,5 +40,5 @@ wontreturn void abort(void) {
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
notpossible;
_Exit(128 + SIGABRT);
}

View file

@ -1,25 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int begin_blocking_operation(void);
void end_blocking_operation(int);
#if SupportsWindows()
#define BEGIN_BLOCKING_OPERATION \
do { \
int _Flags; \
_Flags = begin_blocking_operation()
#define END_BLOCKING_OPERATION \
end_blocking_operation(_Flags); \
} \
while (0)
#else
#define BEGIN_BLOCKING_OPERATION (void)0
#define END_BLOCKING_OPERATION (void)0
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -209,6 +209,7 @@ int madvise(void *, uint64_t, int);
#endif
#ifdef _COSMO_SOURCE
bool32 fdexists(int);
bool32 fileexists(const char *);
bool32 isdirectory(const char *);
bool32 isexecutable(const char *);
@ -232,8 +233,6 @@ int sys_ptrace(int, ...);
int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
int sys_ioprio_get(int, int);
int sys_ioprio_set(int, int, int);
int tgkill(int, int, int);
int tkill(int, int);
int tmpfd(void);
int touch(const char *, unsigned);
int unveil(const char *, const char *);

View file

@ -65,53 +65,11 @@ $(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/foist.o \
o/$(MODE)/libc/calls/__sig2.o \
o/$(MODE)/libc/calls/sigchld-nt.o \
o/$(MODE)/libc/calls/sigwinch-nt.o \
o/$(MODE)/libc/calls/onntconsoleevent.o \
o/$(MODE)/libc/calls/wincrash.o \
o/$(MODE)/libc/calls/ntcontext2linux.o: private \
$(LIBC_CALLS_A_OBJS): private \
COPTS += \
$(NO_MAGIC)
# we can't use asan because:
# siginfo_t memory is owned by kernels
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
COPTS += \
-ffreestanding \
-fno-sanitize=address
# we can't use asan because:
# ucontext_t memory is owned by xnu kernel
o/$(MODE)/libc/calls/sigenter-xnu.o: private \
COPTS += \
-ffreestanding \
-fno-sanitize=address
# we can't use asan because:
# vdso memory is owned by linux kernel
o/$(MODE)/libc/calls/vdsofunc.greg.o: private \
COPTS += \
-ffreestanding \
-fno-sanitize=address
# we can't use magic because:
# this code is called by WinMain
o/$(MODE)/libc/calls/winstdin1.o: private \
COPTS += \
$(NO_MAGIC)
# we can't use asan because:
# ntspawn allocates 128kb of heap memory via win32
o/$(MODE)/libc/calls/ntspawn.o \
o/$(MODE)/libc/calls/mkntcmdline.o \
o/$(MODE)/libc/calls/mkntenvblock.o: private \
COPTS += \
-ffreestanding \
-fno-sanitize=address
-fno-sanitize=all \
-Wframe-larger-than=4096 \
-Walloca-larger-than=4096
ifneq ($(ARCH), aarch64)
# we always want -O3 because:
@ -129,21 +87,6 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: private \
-mstringop-strategy=loop
endif
# we must disable static stack safety because:
# these functions use alloca(n)
o/$(MODE)/libc/calls/execl.o \
o/$(MODE)/libc/calls/execle.o \
o/$(MODE)/libc/calls/execlp.o \
o/$(MODE)/libc/calls/execvpe.o \
o/$(MODE)/libc/calls/statfs.o \
o/$(MODE)/libc/calls/fstatfs.o \
o/$(MODE)/libc/calls/execve-sysv.o \
o/$(MODE)/libc/calls/readlinkat-nt.o \
o/$(MODE)/libc/calls/execve-nt.greg.o \
o/$(MODE)/libc/calls/mkntenvblock.o: private \
CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
# we always want -Os because:
# va_arg codegen is very bloated in default mode
o//libc/calls/open.o \
@ -176,24 +119,6 @@ o/$(MODE)/libc/calls/timeval_frommicros.o: private \
CFLAGS += \
-O2
# privileged functions
o/$(MODE)/libc/calls/sigenter-freebsd.o \
o/$(MODE)/libc/calls/sigenter-netbsd.o \
o/$(MODE)/libc/calls/sigenter-openbsd.o \
o/$(MODE)/libc/calls/sigenter-linux.o \
o/$(MODE)/libc/calls/sigenter-xnu.o \
o/$(MODE)/libc/calls/pledge-linux.o \
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
CFLAGS += \
-ffreestanding \
-fno-sanitize=all \
-fno-stack-protector
o/$(MODE)/libc/calls/pledge-linux.o \
o/$(MODE)/libc/calls/unveil.o: private \
CFLAGS += \
-DSTACK_FRAME_UNLIMITED
ifeq ($(ARCH), aarch64)
o/$(MODE)/libc/calls/sigaction.o: private CFLAGS += -mcmodel=large
o/$(MODE)/libc/calls/getloadavg-nt.o: private CFLAGS += -ffreestanding
@ -215,7 +140,7 @@ o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/switchstacks.o: libc/calls/switchstacks.S
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))

View file

@ -23,6 +23,7 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
@ -49,7 +50,15 @@
int chdir(const char *path) {
int rc;
struct ZiposUri zipname;
GetProgramExecutableName(); // XXX: ugly workaround
if (_weaken(__get_tmpdir)) {
_weaken(__get_tmpdir)();
}
if (_weaken(GetAddr2linePath)) {
_weaken(GetAddr2linePath)();
}
if (_weaken(GetProgramExecutableName)) {
_weaken(GetProgramExecutableName)();
}
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
rc = efault();
} else if (_weaken(__zipos_parseuri) &&

View file

@ -22,5 +22,5 @@
#include "libc/runtime/syslib.internal.h"
int sys_clock_gettime_m1(int clock, struct timespec *ts) {
return _sysret(__syslib->clock_gettime(clock, ts));
return _sysret(__syslib->__clock_gettime(clock, ts));
}

View file

@ -77,18 +77,17 @@
* @vforksafe
*/
int clock_gettime(int clock, struct timespec *ts) {
// threads on win32 stacks call this so we can't asan check *ts
int rc;
if (clock == 127) {
rc = einval(); // 127 is used by consts.sh to mean unsupported
} else if (!ts || (IsAsan() && !__asan_is_valid_timespec(ts))) {
rc = efault();
} else {
rc = __clock_gettime(clock, ts);
}
#if SYSDEBUG
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc);
POLLTRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc);
}
#endif
return rc;

View file

@ -16,43 +16,32 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/bo.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/macros.internal.h"
#include "libc/errno.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#include "third_party/finger/finger.h"
static textwindows int sys_clock_nanosleep_nt_impl(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
struct timespec now, abs;
if (flags & TIMER_ABSTIME) {
abs = *req;
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
timespec_tomillis(timespec_sub(abs, now))),
false);
}
} else {
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
struct timespec abs) {
struct timespec now;
struct PosixThread *pt = _pthread_self();
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
abs = timespec_add(now, *req);
for (;;) {
sys_clock_gettime_nt(clock, &now);
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0)) {
if (rem) *rem = timespec_sub(abs, now);
return -1;
}
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
timespec_tomillis(timespec_sub(abs, now))),
false);
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0)) return -1;
pt->abort_errno = 0;
pt->pt_flags |= PT_INSEMAPHORE;
WaitForSingleObject(pt->semaphore,
timespec_tomillis(timespec_sub(abs, now)));
pt->pt_flags &= ~PT_INSEMAPHORE;
if (pt->abort_errno) {
errno = pt->abort_errno;
return -1;
}
}
}
@ -61,8 +50,17 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
const struct timespec *req,
struct timespec *rem) {
int rc;
BEGIN_BLOCKING_OPERATION;
rc = sys_clock_nanosleep_nt_impl(clock, flags, req, rem);
END_BLOCKING_OPERATION;
struct timespec abs, now;
if (flags & TIMER_ABSTIME) {
abs = *req;
} else {
if (sys_clock_gettime_nt(clock, &now)) return -1;
abs = timespec_add(now, *req);
}
rc = sys_clock_nanosleep_nt_impl(clock, abs);
if (rc == -1 && rem && errno == EINTR) {
sys_clock_gettime_nt(clock, &now);
*rem = timespec_subz(abs, now);
}
return rc;
}

View file

@ -21,12 +21,18 @@
#include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/timeval.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sock/internal.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
struct timespec *rem) {
@ -51,16 +57,25 @@ int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
#else
long res;
struct timespec abs, now, rel;
if (_weaken(pthread_testcancel_np) && //
_weaken(pthread_testcancel_np)()) {
return ecanceled();
}
if (flags & TIMER_ABSTIME) {
abs = *req;
if (!(res = __syslib->clock_gettime(clock, &now))) {
if (!(res = __syslib->__clock_gettime(clock, &now))) {
if (timespec_cmp(abs, now) > 0) {
rel = timespec_sub(abs, now);
res = __syslib->nanosleep(&rel, 0);
res = __syslib->__nanosleep(&rel, 0);
}
}
} else {
res = __syslib->nanosleep(req, rem);
res = __syslib->__nanosleep(req, rem);
}
if (res == -EINTR && //
(_weaken(pthread_testcancel_np) && //
_weaken(pthread_testcancel_np))) {
return ecanceled();
}
return _sysret(res);
#endif

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/calls/asan.internal.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/clock_gettime.internal.h"
#include "libc/calls/cp.internal.h"
@ -78,7 +79,7 @@ static struct timespec GetNanosleepLatency(void) {
clock_gettime_f *cgt;
struct timespec x, y, w = {0, 1};
if (!(nanos = g_nanosleep_latency)) {
BLOCK_CANCELLATIONS;
BLOCK_SIGNALS;
for (cgt = __clock_gettime_get(0);;) {
npassert(!cgt(CLOCK_REALTIME_PRECISE, &x));
rc = sys_clock_nanosleep(CLOCK_REALTIME, 0, &w, 0);
@ -90,7 +91,7 @@ static struct timespec GetNanosleepLatency(void) {
break;
}
}
ALLOW_CANCELLATIONS;
ALLOW_SIGNALS;
}
return timespec_fromnanos(nanos);
}
@ -117,7 +118,7 @@ static errno_t SpinNanosleep(int clock, int flags, const struct timespec *req,
cgt = __clock_gettime_get(0);
npassert(!cgt(CLOCK_REALTIME, &start));
for (;;) {
sched_yield();
pthread_yield();
npassert(!cgt(CLOCK_REALTIME, &now));
if (flags & TIMER_ABSTIME) {
if (timespec_cmp(now, *req) >= 0) {
@ -242,13 +243,11 @@ static bool ShouldUseSpinNanosleep(int clock, int flags,
errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
struct timespec *rem) {
int rc;
// threads on win32 stacks call this so we can't asan check *ts
LOCKTRACE("clock_nanosleep(%s, %s, %s) → ...", DescribeClockName(clock),
DescribeSleepFlags(flags), DescribeTimespec(0, req));
if (IsMetal()) {
rc = ENOSYS;
} else if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
(rem && !__asan_is_valid_timespec(rem))))) {
rc = EFAULT;
} else if (clock == 127 || //
(flags & ~TIMER_ABSTIME) || //
req->tv_sec < 0 || //

View file

@ -29,6 +29,7 @@
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/filetime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/madv.h"
@ -37,17 +38,24 @@
static textwindows int sys_copyfile_nt(const char *src, const char *dst,
int flags) {
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct {
char16_t src16[PATH_MAX];
char16_t dst16[PATH_MAX];
} M;
CheckLargeStackAllocation(&M, sizeof(M));
#pragma GCC pop_options
int64_t fhsrc, fhdst;
struct NtFileTime accessed, modified;
char16_t src16[PATH_MAX], dst16[PATH_MAX];
if (__mkntpath(src, src16) == -1) return -1;
if (__mkntpath(dst, dst16) == -1) return -1;
if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) {
if (__mkntpath(src, M.src16) == -1) return -1;
if (__mkntpath(dst, M.dst16) == -1) return -1;
if (CopyFile(M.src16, M.dst16, !!(flags & COPYFILE_NOCLOBBER))) {
if (flags & COPYFILE_PRESERVE_TIMESTAMPS) {
fhsrc = CreateFile(src16, kNtFileReadAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0);
fhdst = CreateFile(dst16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
fhsrc = CreateFile(M.src16, kNtFileReadAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0);
fhdst = CreateFile(M.dst16, kNtFileWriteAttributes, kNtFileShareRead,
NULL, kNtOpenExisting, kNtFileAttributeNormal, 0);
if (fhsrc != -1 && fhdst != -1) {
GetFileTime(fhsrc, NULL, &accessed, &modified);
SetFileTime(fhdst, NULL, &accessed, &modified);

View file

@ -20,7 +20,7 @@
#include "libc/intrin/atomic.h"
#include "libc/runtime/internal.h"
static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
char t;
size_t a, b, i = 0;
do {
@ -38,7 +38,7 @@ static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
}
// This function is called very early by WinMain().
dontasan textwindows char16_t *__create_pipe_name(char16_t *a) {
textwindows char16_t *__create_pipe_name(char16_t *a) {
char16_t *p = a;
const char *q = "\\\\?\\pipe\\cosmo\\";
static atomic_uint x;

View file

@ -1,35 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 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/struct/timespec.h"
// TODO(jart): DELETE
/**
* Returns seconds since epoch w/ high-precision.
* @param clockid can be CLOCK_{REALTIME,MONOTONIC}, etc.
*/
long double dtime(int clockid) {
long double secs;
struct timespec tv;
clock_gettime(clockid, &tv);
secs = tv.tv_nsec;
secs *= 1e-9;
secs += tv.tv_sec;
return secs;
}

View file

@ -55,6 +55,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
}
if (g_fds.p[newfd].kind) {
sys_close_nt(g_fds.p + newfd, newfd);
bzero(g_fds.p + newfd, sizeof(*g_fds.p));
}
}

View file

@ -167,8 +167,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
}
bool32 ok;
struct NtOverlapped ov = {.hEvent = f->handle,
.Pointer = (void *)(uintptr_t)off};
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = off};
if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) {
@ -254,8 +253,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
// allow a big range to unlock many small ranges
for (flp = &g_locks.list, fl = *flp; fl;) {
if (fl->fd == fd && EncompassesFileLock(fl, off, len)) {
struct NtOverlapped ov = {.hEvent = f->handle,
.Pointer = (void *)(uintptr_t)fl->off};
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
if (UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
*flp = fl->next;
ft = fl->next;
@ -287,14 +285,13 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
off + len >= fl->off && //
off + len < fl->off + fl->len) {
// cleave left side of lock
struct NtOverlapped ov = {.hEvent = f->handle,
.Pointer = (void *)(uintptr_t)fl->off};
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
if (!UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
return -1;
}
fl->len = (fl->off + fl->len) - (off + len);
fl->off = off + len;
ov.Pointer = (void *)(uintptr_t)fl->off;
ov.Pointer = fl->off;
if (!LockFileEx(f->handle, kNtLockfileExclusiveLock, 0, fl->len,
fl->len >> 32, &ov)) {
return -1;

51
libc/calls/fdexists.c Normal file
View file

@ -0,0 +1,51 @@
/*-*- 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-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/f.h"
/**
* Returns true if file descriptor exists.
*
* This function is equivalent to:
*
* fcntl(fd, F_GETFL) != -1
*
* Except it won't clobber `errno` and has minimal linkage.
*/
bool32 fdexists(int fd) {
bool32 res;
if (IsWindows() || IsMetal()) {
int e = errno;
if (__sys_fcntl(fd, F_GETFL) != -1) {
res = true;
} else {
errno = e;
res = false;
}
} else {
res = __isfdopen(fd);
}
STRACE("fdexists(%d) → %hhhd", fd, res);
return res;
}

View file

@ -21,7 +21,7 @@
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/execve.internal.h"
#include "libc/proc/execve.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h"

View file

@ -1,115 +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 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/sig.internal.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/macros.internal.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/sysv/consts/sicode.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls2.internal.h"
#ifdef __x86_64__
// 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
// Both Microsoft and the Fifth Bell System agree on this one.
#define kStackAlign 16
__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(SetThreadContext) *const __imp_SetThreadContext;
__msabi extern typeof(SuspendThread) *const __imp_SuspendThread;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
int WinThreadLaunch(struct Delivery *, long, int (*)(struct Delivery *), long);
static textwindows unsigned long StrLen(const char *s) {
unsigned long n = 0;
while (*s++) ++n;
return n;
}
static textwindows void Log(const char *s) {
#if IsModeDbg()
__imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0);
#endif
}
/**
* Executes signal handler asynchronously inside other thread.
*
* @return 0 on success, or -1 on error
*/
textwindows int _pthread_signal(struct PosixThread *pt, int sig, int sic) {
int rc = -1;
intptr_t th;
if ((th = __imp_OpenThread(
kNtThreadSuspendResume | kNtThreadGetContext, false,
atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)))) {
uint32_t old_suspend_count;
if ((old_suspend_count = __imp_SuspendThread(th)) != -1u) {
if (!old_suspend_count &&
atomic_load_explicit(&pt->status, memory_order_acquire) <
kPosixThreadTerminated) {
struct NtContext nc = {.ContextFlags = kNtContextAll};
struct Delivery pkg = {0, sig, sic, &nc};
if (__imp_GetThreadContext(th, &nc)) {
struct CosmoTib *mytls;
mytls = __get_tls();
__set_tls_win32(pt->tib);
rc = WinThreadLaunch(
&pkg, 0, __sig_tramp,
ROUNDDOWN(nc.Rsp - kRedzoneSize, kStackAlign) - 8);
__imp_SetThreadContext(th, &nc);
__set_tls_win32(mytls);
} else {
Log("GetThreadContext failed\n");
}
}
__imp_ResumeThread(th);
} else {
Log("SuspendThread failed\n");
}
__imp_CloseHandle(th);
} else {
Log("OpenThread failed\n");
}
return rc;
}
#endif /* __x86_64__ */

View file

@ -122,7 +122,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
} else {
st.st_ctim = st.st_mtim;
}
st.st_gid = st.st_uid = __synthesize_uid();
st.st_gid = st.st_uid = sys_getuid_nt();
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
st.st_dev = wst.dwVolumeSerialNumber;
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;

View file

@ -35,10 +35,13 @@
* @cancellationpoint
*/
int fstatfs(int fd, struct statfs *sf) {
int rc;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
union statfs_meta m;
BEGIN_CANCELLATION_POINT;
CheckLargeStackAllocation(&m, sizeof(m));
#pragma GCC pop_options
int rc;
BEGIN_CANCELLATION_POINT;
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsup();

View file

@ -16,14 +16,17 @@
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/struct/rlimit.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/rlimit.h"
/**
* Gets file descriptor table size.
*
* @return current limit on the number of open files per process
* Returns limit on number of open files.
*/
int getdtablesize(void) {
return g_fds.n;
struct rlimit rl = {10000};
getrlimit(RLIMIT_NOFILE, &rl);
return MIN(rl.rlim_cur, INT_MAX);
}

View file

@ -16,12 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nt/runtime.h"
@ -30,124 +32,113 @@
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/ok.h"
#define SIZE 1024
#define CTL_KERN 1
#define KERN_PROC 14
#define KERN_PROC_PATHNAME_FREEBSD 12
#define KERN_PROC_PATHNAME_NETBSD 5
static struct ProgramExecutableName {
_Atomic(uint32_t) once;
char buf[PATH_MAX];
} program_executable_name;
static struct {
atomic_uint once;
union {
char buf[PATH_MAX];
char16_t buf16[PATH_MAX / 2];
} u;
} g_prog;
static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
static inline char *StrCat(char buf[PATH_MAX], const char *a, const char *b) {
char *p, *e;
p = buf;
e = buf + PATH_MAX;
while (*a && p < e) *p++ = *a++;
while (*b && p < e) *p++ = *b++;
return buf;
}
static inline void GetProgramExecutableNameImpl(char *p, char *e) {
int c;
char *q;
char **ep;
ssize_t rc;
size_t i, n;
union {
int cmd[4];
char path[PATH_MAX];
char16_t path16[PATH_MAX];
} u;
static inline void InitProgramExecutableNameImpl(void) {
if (IsWindows()) {
n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16));
for (i = 0; i < n; ++i) {
int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16));
for (int i = 0; i < n; ++i) {
// turn c:\foo\bar into c:/foo/bar
if (u.path16[i] == '\\') {
u.path16[i] = '/';
if (g_prog.u.buf16[i] == '\\') {
g_prog.u.buf16[i] = '/';
}
}
if (IsAlpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') {
if (IsAlpha(g_prog.u.buf16[0]) && //
g_prog.u.buf16[1] == ':' && //
g_prog.u.buf16[2] == '/') {
// turn c:/... into /c/...
u.path16[1] = u.path16[0];
u.path16[0] = '/';
u.path16[2] = '/';
g_prog.u.buf16[1] = g_prog.u.buf16[0];
g_prog.u.buf16[0] = '/';
g_prog.u.buf16[2] = '/';
}
tprecode16to8(p, e - p, u.path16);
tprecode16to8(g_prog.u.buf, sizeof(g_prog.u.buf), g_prog.u.buf16);
return;
}
char c, *q;
if (IsMetal()) {
if (!memccpy(p, APE_COM_NAME, 0, e - p - 1)) {
e[-1] = 0;
}
return;
q = APE_COM_NAME;
goto CopyString;
}
// if argv[0] exists then turn it into an absolute path. we also try
// adding a .com suffix since the ape auto-appends it when resolving
if ((q = __argv[0]) && ((!sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
((q = StrCat(u.path, __argv[0], ".com")) &&
!sys_faccessat(AT_FDCWD, q, F_OK, 0)))) {
if ((q = __argv[0])) {
char *p = g_prog.u.buf;
char *e = p + sizeof(g_prog.u.buf);
if (*q != '/') {
if (q[0] == '.' && q[1] == '/') {
q += 2;
}
if (getcwd(p, e - p)) {
if (getcwd(p, e - p - 1 - 4)) { // for / and .com
while (*p) ++p;
*p++ = '/';
}
}
for (i = 0; *q && p + 1 < e; ++p, ++q) {
*p = *q;
while ((c = *q++)) {
if (p + 1 + 4 < e) { // for nul and .com
*p++ = c;
}
}
*p = 0;
return;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
p = WRITE32LE(p, READ32LE(".com"));
*p = 0;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
}
// if getenv("_") exists then use that
for (ep = __envp; (q = *ep); ++ep) {
for (char **ep = __envp; (q = *ep); ++ep) {
if (*q++ == '_' && *q++ == '=') {
while ((c = *q++)) {
if (p + 1 < e) {
*p++ = c;
}
}
*p = 0;
return;
goto CopyString;
}
}
// if argv[0] doesn't exist, then fallback to interpreter name
if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 ||
(rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) {
p[rc] = 0;
ssize_t got;
char *b = g_prog.u.buf;
size_t n = sizeof(g_prog.u.buf) - 1;
if ((got = sys_readlinkat(AT_FDCWD, "/proc/self/exe", b, n)) > 0 ||
(got = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", b, n)) > 0) {
b[got] = 0;
return;
}
if (IsFreebsd() || IsNetbsd()) {
u.cmd[0] = CTL_KERN;
u.cmd[1] = KERN_PROC;
int cmd[4];
cmd[0] = CTL_KERN;
cmd[1] = KERN_PROC;
if (IsFreebsd()) {
u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD;
cmd[2] = KERN_PROC_PATHNAME_FREEBSD;
} else {
u.cmd[2] = KERN_PROC_PATHNAME_NETBSD;
cmd[2] = KERN_PROC_PATHNAME_NETBSD;
}
u.cmd[3] = -1; // current process
n = e - p;
if (sys_sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) {
cmd[3] = -1; // current process
if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) {
return;
}
}
// otherwise give up and just copy argv[0] into it
if (!*p && (q = __argv[0])) {
// give up and just copy argv[0] into it
if ((q = __argv[0])) {
CopyString:
char *p = g_prog.u.buf;
char *e = p + sizeof(g_prog.u.buf);
while ((c = *q++)) {
if (p + 1 < e) {
*p++ = c;
@ -155,14 +146,14 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
}
*p = 0;
}
// if we don't even have that then empty the string
g_prog.u.buf[0] = 0;
}
static void InitProgramExecutableName(void) {
int e;
e = errno;
GetProgramExecutableNameImpl(
program_executable_name.buf,
program_executable_name.buf + sizeof(program_executable_name.buf));
int e = errno;
InitProgramExecutableNameImpl();
errno = e;
}
@ -170,6 +161,6 @@ static void InitProgramExecutableName(void) {
* Returns absolute path of program.
*/
char *GetProgramExecutableName(void) {
cosmo_once(&program_executable_name.once, InitProgramExecutableName);
return program_executable_name.buf;
cosmo_once(&g_prog.once, InitProgramExecutableName);
return g_prog.u.buf;
}

View file

@ -40,7 +40,7 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
if (!usage) return 0;
me = GetCurrentProcess();
if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
who == RUSAGE_SELF ? GetCurrentProcess() : GetCurrentThread(),
&ftCreation, &ftExit, &ftKernel, &ftUser) ||
!GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) ||
!GetProcessIoCounters(me, &iocount)) {

View file

@ -26,7 +26,7 @@
axdx_t sys_gettimeofday_m1(struct timeval *tv, struct timezone *tz, void *wut) {
axdx_t ad;
struct timespec ts;
ad.ax = _sysret(__syslib->clock_gettime(CLOCK_REALTIME, &ts));
ad.ax = _sysret(__syslib->__clock_gettime(CLOCK_REALTIME, &ts));
ad.dx = 0;
if (!ad.ax && tv) {
*tv = timespec_totimeval(ts);

View file

@ -49,21 +49,15 @@ static gettimeofday_f *__gettimeofday = __gettimeofday_init;
*
* @param tv points to timeval that receives result if non-NULL
* @param tz receives UTC timezone if non-NULL
* @error EFAULT if `tv` or `tz` isn't valid memory
* @see clock_gettime() for nanosecond precision
* @see strftime() for string formatting
*/
int gettimeofday(struct timeval *tv, struct timezone *tz) {
int rc;
if (IsAsan() && ((tv && !__asan_is_valid(tv, sizeof(*tv))) ||
(tz && !__asan_is_valid(tz, sizeof(*tz))))) {
rc = efault();
} else {
rc = __gettimeofday(tv, tz, 0).ax;
}
int rc = __gettimeofday(tv, tz, 0).ax;
#if SYSDEBUG
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
POLLTRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz,
rc);
}
#endif
return rc;

View file

@ -22,6 +22,8 @@
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
// asan must be disabled because __proc_worker calls this on win32 stack
static uint32_t __kmp32(const void *buf, size_t size) {
size_t i;
uint32_t h;
@ -31,7 +33,7 @@ static uint32_t __kmp32(const void *buf, size_t size) {
return h;
}
textwindows uint32_t __synthesize_uid(void) {
textwindows uint32_t sys_getuid_nt(void) {
char16_t buf[257];
static atomic_uint uid;
uint32_t tmp, size = ARRAYLEN(buf);

View file

@ -47,7 +47,7 @@ uint32_t getuid(void) {
} else if (!IsWindows()) {
rc = sys_getuid();
} else {
rc = __synthesize_uid();
rc = sys_getuid_nt();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getuid", rc);
@ -72,7 +72,7 @@ uint32_t getgid(void) {
} else if (!IsWindows()) {
rc = sys_getgid();
} else {
rc = __synthesize_uid();
rc = sys_getuid_nt();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getgid", rc);

View file

@ -6,11 +6,8 @@
#include "libc/dce.h"
#include "libc/macros.internal.h"
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
#define kSigOpRestartable 1
#define kSigOpNochld 2
#define kSigOpUnmaskable 4
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -27,7 +24,7 @@ void __releasefd(int);
int __ensurefds(int);
int __ensurefds_unlocked(int);
void __printfds(void);
uint32_t __synthesize_uid(void);
uint32_t sys_getuid_nt(void);
forceinline int64_t __getfdhandleactual(int fd) {
return g_fds.p[fd].handle;

View file

@ -18,35 +18,28 @@
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/struct/windowplacement.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
textwindows int _check_interrupts(int sigops) {
int e, rc, flags = 0;
e = errno;
int status;
errno_t err;
struct PosixThread *pt = _pthread_self();
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
(err = _weaken(pthread_testcancel_np)())) {
goto Interrupted;
}
if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) {
if (status == 2 && (sigops & kSigOpRestartable)) {
return 0;
}
err = EINTR;
Interrupted:
pt->abort_errno = errno = err;
return -1;
}
if (__tls_enabled) {
flags = __get_tls()->tib_flags;
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
}
if (_weaken(_check_sigalrm)) {
_weaken(_check_sigalrm)();
}
if (__tls_enabled) {
__get_tls()->tib_flags = flags;
}
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
return eintr();
}
errno = e;
return 0;
}

View file

@ -40,6 +40,7 @@
#include "libc/nt/struct/ipadapteraddresses.h"
#include "libc/nt/winsock.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sock/internal.h"
#include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/ifreq.h"
@ -485,8 +486,12 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
if (IsLinux()) {
return sys_ioctl(fd, SIOCGIFCONF, ifc);
}
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
bufMax = 15000; /* conservative guesstimate */
b = alloca(bufMax);
CheckLargeStackAllocation(b, bufMax);
#pragma GCC pop_options
memcpy(ifcBsd, &bufMax, 8); /* ifc_len */
memcpy(ifcBsd + (IsXnu() ? 4 : 8), &b, 8); /* ifc_buf */
if ((rc = sys_ioctl(fd, SIOCGIFCONF, &ifcBsd)) != -1) {

View file

@ -29,7 +29,7 @@
* @vforksafe
*/
bool32 isexecutable(const char *path) {
struct stat st; /* execve() depends on this */
struct stat st;
if (fstatat(AT_FDCWD, path, &st, 0)) return 0;
return !S_ISDIR(st.st_mode) && !!(st.st_mode & 0111);
}

View file

@ -1,123 +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 2021 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/dce.h"
#include "libc/macros.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/ctrlevent.h"
#include "libc/nt/enum/processaccess.h"
#include "libc/nt/enum/th32cs.h"
#include "libc/nt/errors.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processentry32.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
static inline int GetConsoleCtrlEvent(int sig) {
switch (sig) {
case SIGINT:
return kNtCtrlCEvent;
case SIGQUIT:
return kNtCtrlBreakEvent;
default:
return -1;
}
}
textwindows int sys_kill_nt(int pid, int sig) {
bool32 ok;
int64_t h;
int event, ntpid;
// is killing everything except init really worth supporting?
if (pid == -1) return einval();
// XXX: NT doesn't really have process groups. For instance the
// CreateProcess() flag for starting a process group actually
// just does an "ignore ctrl-c" internally.
pid = ABS(pid);
// If we're targeting current process group then just call raise().
if (!pid || pid == getpid()) {
if (!sig) return 0; // ability check passes
return raise(sig);
}
// GenerateConsoleCtrlEvent() will always signal groups and there's
// nothing we can do about it, unless we have a GUI GetMessage loop
// and alternatively create a centralized signal daemon like cygwin
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
// we're killing with SIGINT or SIGQUIT which are the only two
// signals we can really use, since TerminateProcess() makes
// everything else effectively a SIGKILL ;_;
if (__isfdkind(pid, kFdProcess)) {
ntpid = GetProcessId(g_fds.p[pid].handle);
} else if (!__isfdopen(pid)) {
ntpid = pid; // XXX: suboptimal
} else {
return esrch();
}
if (GenerateConsoleCtrlEvent(event, ntpid)) {
return 0;
} else {
return -1;
}
}
// is this a cosmo pid that was returned by fork?
if (__isfdkind(pid, kFdProcess)) {
if (!sig) return 0; // ability check passes
// since windows can't execve we need to kill the grandchildren
// TODO(jart): should we just kill the whole tree too? there's
// no obvious way to tell if it's the execve shell
struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)};
ntpid = GetProcessId(g_fds.p[pid].handle);
int64_t hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
if (Process32First(hSnap, &pe)) {
do {
if (pe.th32ParentProcessID == ntpid) {
if ((h = OpenProcess(kNtProcessTerminate, false, pe.th32ProcessID))) {
TerminateProcess(h, sig);
CloseHandle(h);
}
}
} while (Process32Next(hSnap, &pe));
}
CloseHandle(hSnap);
ok = TerminateProcess(g_fds.p[pid].handle, sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
return 0;
}
// XXX: Is this a raw new technology pid? Because that's messy.
if ((h = OpenProcess(kNtProcessTerminate, false, pid))) {
if (sig) {
ok = TerminateProcess(h, sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) {
ok = true; // cargo culting other codebases here
}
}
CloseHandle(h);
return 0;
} else {
return -1;
}
}

View file

@ -20,17 +20,24 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/stack.h"
textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) {
char16_t newpath16[PATH_MAX];
char16_t oldpath16[PATH_MAX];
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) != -1 &&
__mkntpathat(newdirfd, newpath, 0, newpath16) != -1) {
if (CreateHardLink(newpath16, oldpath16, NULL)) {
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct {
char16_t newpath16[PATH_MAX];
char16_t oldpath16[PATH_MAX];
} M;
CheckLargeStackAllocation(&M, sizeof(M));
#pragma GCC pop_options
if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) != -1 &&
__mkntpathat(newdirfd, newpath, 0, M.newpath16) != -1) {
if (CreateHardLink(M.newpath16, M.oldpath16, NULL)) {
return 0;
} else {
return __fix_enotdir3(__winerr(), newpath16, oldpath16);
return __fix_enotdir3(__winerr(), M.newpath16, M.oldpath16);
}
} else {
return -1;

View file

@ -16,21 +16,63 @@
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/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/nt/struct/byhandlefileinformation.h"
#include "libc/stdckdint.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
int64_t res;
if (__isfdkind(fd, kFdFile)) {
if (GetFileType(g_fds.p[fd].handle) != kNtFileTypePipe) {
if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) {
return res;
} else {
static textwindows int64_t GetPosition(struct Fd *f, int whence) {
switch (whence) {
case SEEK_SET:
return 0;
case SEEK_CUR:
return f->pointer;
case SEEK_END: {
struct NtByHandleFileInformation wst;
if (!GetFileInformationByHandle(f->handle, &wst)) {
return __winerr();
}
return (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
}
default:
return einval();
}
}
static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) {
int64_t pos;
if ((pos = GetPosition(f, whence)) != -1) {
if (!ckd_add(&pos, pos, offset)) {
if (pos >= 0) {
return pos;
} else {
return einval();
}
} else {
return eoverflow();
}
} else {
return -1;
}
}
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
if (__isfdkind(fd, kFdFile)) {
struct Fd *f = g_fds.p + fd;
int filetype = GetFileType(f->handle);
if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) {
int64_t res;
pthread_mutex_lock(&f->lock);
if ((res = Seek(f, offset, whence)) != -1) {
f->pointer = res;
}
pthread_mutex_unlock(&f->lock);
return res;
} else {
return espipe();
}

View file

@ -52,7 +52,7 @@ __static_yoink("_init_metalfile");
void *__ape_com_base;
size_t __ape_com_size = 0;
textstartup dontasan void InitializeMetalFile(void) {
textstartup void InitializeMetalFile(void) {
if (IsMetal()) {
/*
* Copy out a pristine image of the program before the program might

View file

@ -35,6 +35,7 @@
* with random text on success (and not modified on error)
* @return pointer to template on success, or NULL w/ errno
* @raise EINVAL if template didn't end with XXXXXX
* @cancellationpoint
*/
char *mkdtemp(char *template) {
int n;

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/ntspawn.h"
#include "libc/proc/ntspawn.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/ntspawn.h"
#include "libc/proc/ntspawn.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/getenv.internal.h"
@ -25,6 +25,7 @@
#include "libc/mem/arraylist2.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
#include "libc/str/utf16.h"
@ -141,7 +142,12 @@ textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[],
bool have_systemroot = false;
size_t i, j, k, n, m, bufi = 0;
for (n = 0; envp[n];) n++;
vars = alloca((n + 1) * sizeof(char *));
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
int nbytes = (n + 1) * sizeof(char *);
vars = alloca(nbytes);
CheckLargeStackAllocation(vars, nbytes);
#pragma GCC pop_options
for (i = 0; i < n; ++i) {
InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot);
}

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/temp.h"
#include "libc/sysv/consts/at.h"
#include "libc/temp.h"
/**
* Creates temporary file name and file descriptor, e.g.
@ -34,6 +34,7 @@
* @see mkstemp() if you don't need flags
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
* @cancellationpoint
*/
int mkostemp(char *template, unsigned flags) {
return openatemp(AT_FDCWD, template, 0, flags, 0);

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/temp.h"
#include "libc/sysv/consts/at.h"
#include "libc/temp.h"
/**
* Creates temporary file name and file descriptor, e.g.
@ -35,6 +35,7 @@
* @see mkostemp() if you don't need suffix
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
* @cancellationpoint
*/
int mkostemps(char *template, int suffixlen, unsigned flags) {
return openatemp(AT_FDCWD, template, suffixlen, flags, 0);

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/temp.h"
#include "libc/sysv/consts/at.h"
#include "libc/temp.h"
/**
* Creates temporary file name and file descriptor, e.g.
@ -34,6 +34,7 @@
* @see mkstemps() if you you need a suffix
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
* @cancellationpoint
*/
int mkstemp(char *template) {
return openatemp(AT_FDCWD, template, 0, 0, 0);

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/temp.h"
#include "libc/sysv/consts/at.h"
#include "libc/temp.h"
/**
* Creates temporary file name and file descriptor, e.g.
@ -36,6 +36,7 @@
* @see mkstemp() if you don't need `suffixlen`
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
* @cancellationpoint
*/
int mkstemps(char *template, int suffixlen) {
return openatemp(AT_FDCWD, template, suffixlen, 0, 0);

View file

@ -36,6 +36,7 @@
* @see mkstemps() if you you need a file extension
* @see openatemp() for one temp roller to rule them all
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
* @cancellationpoint
*/
char *mktemp(char *template) {
int fd;

View file

@ -20,7 +20,7 @@
#include "libc/runtime/pc.internal.h"
#ifdef __x86_64__
dontasan int sys_munmap_metal(void *addr, size_t size) {
int sys_munmap_metal(void *addr, size_t size) {
size_t i;
uint64_t *e, paddr;
struct mman *mm = __get_mm();

View file

@ -46,6 +46,6 @@ int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) {
}
return rc;
#else
return _sysret(__syslib->nanosleep(req, rem));
return _sysret(__syslib->__nanosleep(req, rem));
#endif
}

View file

@ -1,117 +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/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/clock_gettime.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/tls.h"
#include "libc/time/time.h"
// TODO(jart): DELETE
static clock_gettime_f *__gettime;
static struct Now {
uint64_t k0;
long double r0, cpn;
} g_now;
static long double GetTimeSample(void) {
uint64_t tick1, tick2;
long double time1, time2;
sched_yield();
time1 = dtime(CLOCK_REALTIME_PRECISE);
tick1 = rdtsc();
nanosleep(&(struct timespec){0, 1000000}, NULL);
time2 = dtime(CLOCK_REALTIME_PRECISE);
tick2 = rdtsc();
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
}
static long double MeasureNanosPerCycle(void) {
int i, n;
long double avg, samp;
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (IsWindows()) {
n = 10;
} else {
n = 5;
}
for (avg = 1.0L, i = 1; i < n; ++i) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}
void RefreshTime(void) {
struct Now now;
now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME_PRECISE);
now.k0 = rdtsc();
memcpy(&g_now, &now, sizeof(now));
}
static long double nowl_sys(void) {
return dtime(CLOCK_REALTIME_PRECISE);
}
static long double nowl_art(void) {
uint64_t ticks = rdtsc() - g_now.k0;
return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn));
}
static long double nowl_vdso(void) {
long double secs;
struct timespec tv;
unassert(__gettime);
__gettime(CLOCK_REALTIME_PRECISE, &tv);
secs = tv.tv_nsec;
secs *= 1 / 1e9L;
secs += tv.tv_sec;
return secs;
}
long double nowl_setup(void) {
bool isfast;
__gettime = __clock_gettime_get(&isfast);
if (isfast) {
nowl = nowl_vdso;
} else if (X86_HAVE(INVTSC)) {
RefreshTime();
nowl = nowl_art;
} else {
nowl = nowl_sys;
}
return nowl();
}
long double (*nowl)(void) = nowl_setup;

View file

@ -1,84 +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/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/tls.h"
#include "libc/time/time.h"
// TODO(jart): DELETE
static struct Now {
bool once;
uint64_t k0;
long double r0, cpn;
} g_now;
static long double GetTimeSample(void) {
uint64_t tick1, tick2;
long double time1, time2;
sched_yield();
time1 = dtime(CLOCK_MONOTONIC);
tick1 = rdtsc();
nanosleep(&(struct timespec){0, 1000000}, NULL);
time2 = dtime(CLOCK_MONOTONIC);
tick2 = rdtsc();
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
}
static long double MeasureNanosPerCycle(void) {
int i, n;
long double avg, samp;
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
if (IsWindows()) {
n = 30;
} else {
n = 20;
}
for (avg = 1.0L, i = 1; i < n; ++i) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}
static void Refresh(void) {
struct Now now;
now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME);
now.k0 = rdtsc();
now.once = true;
memcpy(&g_now, &now, sizeof(now));
}
long double ConvertTicksToNanos(double ticks) {
if (!g_now.once) Refresh();
return ticks * g_now.cpn; /* pico scale */
}

View file

@ -1,114 +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/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/nt/enum/ctrlevent.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.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"
#ifdef __x86_64__
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
static textwindows int GetSig(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
return SIGINT;
case kNtCtrlBreakEvent:
return SIGQUIT;
case kNtCtrlCloseEvent:
case kNtCtrlLogoffEvent: // only received by services
case kNtCtrlShutdownEvent: // only received by services
return SIGHUP;
default:
return SIGSTKFLT;
}
}
textwindows bool32 __sig_notify(int sig, int sic) {
// 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.
if (__sighandrvas[sig] == (uintptr_t)SIG_IGN) {
return true;
}
// 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) {
__sig.pending |= 1ull << (sig - 1);
return true;
}
pthread_spin_lock(&_pthread_lock);
// before we get asynchronous, let's try to find a thread that is
// currently blocked on io which we can interrupt with our signal
// this is important, because if we we asynchronously interrupt a
// thread that's calling ReadFile() by suspending / resuming then
// the io operation will report end of file (why) upon resumation
struct Dll *e;
for (e = dll_first(_pthread_list); e; 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; // masked
if (pt->flags & PT_BLOCKED) {
pthread_spin_unlock(&_pthread_lock);
__sig.pending |= 1ull << (sig - 1);
return true;
}
}
// limbo means most of the cosmo runtime is totally broken, 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
// going asynchronous is heavy but it's the only way to stop code
// that does stuff like scientific computing, which are cpu-bound
for (e = dll_first(_pthread_list); e; 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; // masked
pthread_spin_unlock(&_pthread_lock);
_pthread_signal(pt, sig, sic);
return true;
}
pthread_spin_unlock(&_pthread_lock);
return true;
}
__msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
__sig_notify(GetSig(dwCtrlType), SI_KERNEL);
return true;
}
#endif /* __x86_64__ */

View file

@ -1,28 +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
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/dce.h"
#include "libc/macros.internal.h"
.init.start 300,_init_onntconsoleevent
testb IsWindows()
jz 1f
ezlea __onntconsoleevent,cx
pushpop 1,%rdx
ntcall __imp_SetConsoleCtrlHandler
1: .init.end 300,_init_onntconsoleevent,globl,hidden

View file

@ -161,8 +161,9 @@ static textwindows int sys_open_nt_console(int dirfd,
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
kNtFileFlagOverlapped)) != -1) {
g_fds.p[fd].extra = sys_open_nt_impl(
dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode, 0);
g_fds.p[fd].extra =
sys_open_nt_impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY,
mode, kNtFileFlagOverlapped);
npassert(g_fds.p[fd].extra != -1);
} else {
return -1;
@ -176,8 +177,8 @@ static textwindows int sys_open_nt_console(int dirfd,
static textwindows int sys_open_nt_file(int dirfd, const char *file,
uint32_t flags, int32_t mode,
size_t fd) {
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, 0)) !=
-1) {
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode,
kNtFileFlagOverlapped)) != -1) {
g_fds.p[fd].kind = kFdFile;
g_fds.p[fd].flags = flags;
g_fds.p[fd].mode = mode;

View file

@ -86,9 +86,7 @@
* @param template is a pathname relative to current directory by default,
* that needs to have "XXXXXX" at the end of the string; this memory
* must be mutable and should be owned by the calling thread; it will
* be modified (only on success) to return the generated filename; it
* is recommended that the caller use `kTmpPath` at the beginning of
* the generated `template` path and then set `dirfd` to `AT_FDCWD`
* be modified (only on success) to return the generated filename
* @param suffixlen may be nonzero to permit characters after the "XXXXXX"
* @param mode is conventionally 0600, for owner-only non-exec access
* @param flags could have O_APPEND, O_CLOEXEC, O_UNLINK, O_SYNC, etc.
@ -97,6 +95,7 @@
* @raise EINVAL if `template` (less the `suffixlen` region) didn't
* end with the string "XXXXXXX"
* @raise EINVAL if `suffixlen` was negative or too large
* @cancellationpoint
*/
int openatemp(int dirfd, char *template, int suffixlen, int flags, int mode) {
flags &= ~O_ACCMODE;

View file

@ -16,37 +16,27 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/bo.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/errors.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
textwindows int sys_pause_nt(void) {
BEGIN_BLOCKING_OPERATION;
for (;;) {
if (_check_interrupts(0)) {
return -1;
int rc;
while (!(rc = _check_interrupts(0))) {
struct PosixThread *pt = _pthread_self();
pt->abort_errno = 0;
pt->pt_flags |= PT_INSEMAPHORE;
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
pt->pt_flags &= ~PT_INSEMAPHORE;
if (pt->abort_errno) {
errno = pt->abort_errno;
rc = -1;
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
POLLTRACE("IOCP EINTR"); // in case we ever figure it out
continue;
}
#if defined(SYSDEBUG) && _POLLTRACE
long ms = 0, totoms = 0;
ms += __SIG_POLLING_INTERVAL_MS;
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
totoms += ms, ms = 0;
POLLTRACE("... pausing for %'lums...", totoms);
}
#endif
}
END_BLOCKING_OPERATION;
return rc;
}

View file

@ -1078,81 +1078,81 @@ static privileged int GetTid(void) {
return res;
}
static privileged long Write(int fd, const void *p, unsigned long n) {
#ifdef __x86_64__
long res;
asm volatile("syscall"
: "=a"(res)
: "0"(__NR_linux_write), "D"(2), "S"(p), "d"(n)
: "rcx", "r11", "memory");
return res;
#elif defined(__aarch64__)
register long x0 asm("x0") = 2;
register long x1 asm("x1") = (long)p;
register long x2 asm("x2") = n;
register long x8 asm("x8") = __NR_linux_write;
asm volatile("svc\t0" : "+r"(x0) : "r"(x1), "r"(x2), "r"(x8) : "memory");
return x0;
#endif
}
static privileged void Log(const char *s, ...) {
va_list va;
va_start(va, s);
do {
#ifdef __x86_64__
int res;
asm volatile("syscall"
: "=a"(res)
: "0"(__NR_linux_write), "D"(2), "S"(s), "d"(StrLen(s))
: "rcx", "r11", "memory");
#elif defined(__aarch64__)
register long r0 asm("x0") = 2;
register long r1 asm("x1") = (long)s;
register long r2 asm("x2") = StrLen(s);
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_write), "r"(r0), "r"(r1), "r"(r2)
: "x8", "memory");
#endif
Write(2, s, StrLen(s));
} while ((s = va_arg(va, const char *)));
va_end(va);
}
static privileged int SigAction(int sig, struct sigaction *act,
struct sigaction *old) {
int res;
act->sa_flags |= Sa_Restorer;
act->sa_restorer = &__restore_rt;
#ifdef __x86_64__
int res;
asm volatile("mov\t%5,%%r10\n\t"
"syscall"
: "=a"(res)
: "0"(__NR_linux_sigaction), "D"(sig), "S"(act), "d"(old), "g"(8)
: "rcx", "r10", "r11", "memory");
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)sig;
register long r1 asm("x1") = (long)act;
register long r2 asm("x2") = (long)old;
register long r3 asm("x3") = (long)8;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_sigaction), "r"(r0), "r"(r1), "r"(r2), "r"(r3)
: "x8", "memory");
res = res_x0;
#endif
return res;
#elif defined(__aarch64__)
register int x0 asm("x0") = sig;
register void *x1 asm("x1") = act;
register void *x2 asm("x2") = old;
register int x3 asm("x3") = 8;
register int x8 asm("x8") = __NR_linux_sigaction;
asm volatile("svc\t0"
: "+r"(x0)
: "r"(x1), "r"(x2), "r"(x3), "r"(x8)
: "memory");
return x0;
#endif
}
static privileged int SigProcMask(int how, int64_t set, int64_t *old) {
int res;
#ifdef __x86_64__
int res;
asm volatile("mov\t%5,%%r10\n\t"
"syscall"
: "=a"(res)
: "0"(__NR_linux_sigprocmask), "D"(how), "S"(&set), "d"(old),
"g"(8)
: "rcx", "r10", "r11", "memory");
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)how;
register long r1 asm("x1") = (long)set;
register long r2 asm("x2") = (long)old;
register long r3 asm("x3") = (long)8;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_sigprocmask), "r"(r0), "r"(r1), "r"(r2), "r"(r3)
: "x8", "memory");
res = res_x0;
#endif
return res;
#elif defined(__aarch64__)
register int x0 asm("x0") = how;
register void *x1 asm("x1") = &set;
register void *x2 asm("x2") = old;
register int x3 asm("x3") = 8;
register long x8 asm("x8") = __NR_linux_sigprocmask;
asm volatile("svc\t0"
: "+r"(x0)
: "r"(x1), "r"(x2), "r"(x3), "r"(x8)
: "memory");
return x0;
#endif
}
static privileged void KillThisProcess(void) {
@ -1205,6 +1205,16 @@ static privileged void KillThisThread(void) {
: "0"(__NR_linux_tkill), "D"(GetTid()), "S"(Sigabrt)
: "rcx", "r11", "memory");
#elif defined(__aarch64__)
{
register long r0 asm("x0") = (long)GetTid();
register long r1 asm("x1") = (long)Sigabrt;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_tkill), "r"(r0), "r"(r1)
: "x8", "memory");
}
#endif
SigProcMask(Sig_Setmask, 0, 0);
#ifdef __x86_64__
@ -1267,6 +1277,7 @@ static privileged void OnSigSys(int sig, siginfo_t *si, void *vctx) {
}
switch (mode & PLEDGE_PENALTY_MASK) {
case PLEDGE_PENALTY_KILL_PROCESS:
KillThisThread();
KillThisProcess();
// fallthrough
case PLEDGE_PENALTY_KILL_THREAD:
@ -2290,10 +2301,13 @@ static privileged void AppendPledge(struct Filter *f, //
* @vforksafe
*/
privileged int sys_pledge_linux(unsigned long ipromises, int mode) {
int i, rc = -1;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct Filter f;
struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)};
CheckLargeStackAllocation(&f, sizeof(f));
#pragma GCC pop_options
struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)};
int i, rc = -1;
f.n = 0;
// set up the seccomp filter

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/prctl.internal.h"
@ -57,15 +58,7 @@
* self-imposed security model. That works best when programs perform
* permission-hungry operations (e.g. calling GetSymbolTable) towards
* the beginning of execution, and then relinquish privilege afterwards
* by calling pledge(). Here's an example of where that matters. Your
* Cosmopolitan C Library needs to code morph your executable in memory
* once you start using threads. But that's only possible to do if you
* used the `prot_exec` promise. So the right thing to do here, is to
* call __enable_threads() before calling pledge() to force it early.
*
* __enable_threads();
* ShowCrashReports();
* pledge("...", 0);
* by calling pledge().
*
* By default exit() is allowed. This is useful for processes that
* perform pure computation and interface with the parent via shared
@ -260,7 +253,9 @@ int pledge(const char *promises, const char *execpromises) {
if (IsGenuineBlink()) return enosys();
if (IsOpenbsd()) return sys_pledge(0, 0);
if (!IsLinux()) return enosys();
if (!(rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0))) return 0;
rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
if (rc == 0 || rc == 2) return 0; // 2 means we're already filtered
unassert(rc < 0);
errno = -rc;
return -1;
} else if (!IsTiny() && IsGenuineBlink()) {

View file

@ -21,6 +21,7 @@
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
@ -42,6 +43,7 @@
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
@ -161,31 +163,31 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
// if we haven't found any good results yet then here we
// compute a small time slice we don't mind sleeping for
waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLLING_INTERVAL_MS, *ms);
waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLL_INTERVAL_MS, *ms);
if (sn) {
// we need to poll the socket handles separately because
// microsoft certainly loves to challenge us with coding
// please note that winsock will fail if we pass zero fd
#if _NTTRACE
POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms);
#endif
if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) {
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
rc = __winsockerr();
goto ReturnPath;
}
*ms -= waitfor;
} else {
gotsocks = 0;
if (!gotinvals && !gotpipes && waitfor) {
// if we've only got pipes and none of them are ready
// then we'll just explicitly sleep for the time left
POLLTRACE("SleepEx(%'d, false) out of %'lu", waitfor, *ms);
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
POLLTRACE("IOCP EINTR");
} else {
*ms -= waitfor;
}
}
if (!gotinvals && !gotsocks && !gotpipes && waitfor) {
POLLTRACE("sleeping for %'d out of %'lu ms", waitfor, *ms);
struct PosixThread *pt = _pthread_self();
pt->abort_errno = 0;
pt->pt_flags |= PT_INSEMAPHORE;
WaitForSingleObject(pt->semaphore, waitfor);
pt->pt_flags &= ~PT_INSEMAPHORE;
if (pt->abort_errno) {
errno = pt->abort_errno;
rc = -1;
goto ReturnPath;
}
*ms -= waitfor; // todo: make more resilient
}
// we gave all the sockets and all the named pipes a shot
// if we found anything at all then it's time to end work

View file

@ -50,7 +50,6 @@ void __printfds(void) {
for (i = 0; i < g_fds.n; ++i) {
if (!g_fds.p[i].kind) continue;
kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind));
if (g_fds.p[i].zombie) kprintf(" zombie");
if (g_fds.p[i].flags) kprintf(" flags=%#x", g_fds.p[i].flags);
if (g_fds.p[i].mode) kprintf(" mode=%#o", g_fds.p[i].mode);
if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle);

View file

@ -21,52 +21,33 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#include "libc/thread/xnu.internal.h"
static dontubsan void RaiseSigFpe(void) {
volatile int x = 0;
x = 1 / x;
}
/**
* Sends signal to self.
*
* This is basically the same as:
*
* tkill(gettid(), sig);
* pthread_kill(pthread_self(), sig);
*
* Note `SIG_DFL` still results in process death for most signals.
*
* This function is not entirely equivalent to kill() or tkill(). For
* example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that
* helps us support Windows. So if the raised signal has a signal
* handler, then the reported `si_code` might not be `SI_TKILL`.
*
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
* @return 0 if signal was delivered and returned, or -1 w/ errno
* @return 0 on success, or nonzero on failure
* @asyncsignalsafe
*/
int raise(int sig) {
int rc;
STRACE("raise(%G) → ...", sig);
if (sig == SIGTRAP) {
DebugBreak();
if (IsXnuSilicon()) {
rc = __syslib->__raise(sig);
} else if (IsWindows()) {
__sig_raise(sig, SI_TKILL);
rc = 0;
#ifndef __aarch64__
} else if (sig == SIGFPE) {
// TODO(jart): Why doesn't AARCH64 raise SIGFPE?
RaiseSigFpe();
rc = 0;
#endif
} else {
rc = tkill(gettid(), sig);
rc = sys_tkill(gettid(), sig, 0);
}
STRACE("...raise(%G) → %d% m", sig, rc);
STRACE("raise(%G) → %d% m", sig, rc);
return rc;
}

View file

@ -16,8 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/bo.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -49,6 +47,8 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
__static_yoink("WinMainStdin");
@ -56,12 +56,6 @@ __static_yoink("WinMainStdin");
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
static textwindows void sys_read_nt_abort(int64_t handle,
struct NtOverlapped *overlapped) {
unassert(CancelIoEx(handle, overlapped) ||
GetLastError() == kNtErrorNotFound);
}
textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
int64_t offset) {
@ -69,20 +63,37 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
bool32 ok;
struct Fd *f;
uint32_t got;
int filetype;
int64_t handle;
uint32_t remain;
char *targetdata;
uint32_t targetsize;
bool is_console_input;
int abort_errno = EAGAIN;
struct PosixThread *pt;
f = g_fds.p + fd;
handle = __resolve_stdin_handle(f->handle);
pt = _pthread_self();
bool pwriting = offset != -1;
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
bool nonblock = !!(f->flags & O_NONBLOCK);
pt->abort_errno = EAGAIN;
if (pwriting && !seekable) {
return espipe();
}
if (!pwriting) {
offset = 0;
}
uint32_t dwConsoleMode;
bool is_console_input =
g_fds.stdin.handle
? f->handle == g_fds.stdin.handle
: !seekable && (f->kind == kFdConsole ||
GetConsoleMode(handle, &dwConsoleMode));
StartOver:
size = MIN(size, 0x7ffff000);
handle = __resolve_stdin_handle(f->handle);
filetype = GetFileType(handle);
is_console_input = g_fds.stdin.handle ? f->handle == g_fds.stdin.handle
: f->handle == g_fds.p[0].handle;
// the caller might be reading a single byte at a time. but we need to
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
@ -103,78 +114,61 @@ StartOver:
targetsize = size;
}
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
struct NtOverlapped overlap = {0};
if (offset != -1) {
// pread() and pwrite() should not be called on a pipe or tty
return espipe();
if (!pwriting && seekable) {
pthread_mutex_lock(&f->lock);
offset = f->pointer;
}
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
.Pointer = offset};
// the win32 manual says it's important to *not* put &got here
// since for overlapped i/o, we always use GetOverlappedResult
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
BlockingOperation:
if (!nonblock) {
pt->ioverlap = &overlap;
pt->iohandle = handle;
}
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(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
BEGIN_BLOCKING_OPERATION;
// 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
// otherwise wait for i/o periodically checking interrupts
if (f->flags & O_NONBLOCK) {
sys_read_nt_abort(handle, &overlap);
} else if (_check_interrupts(kSigOpRestartable)) {
Interrupted:
abort_errno = errno;
sys_read_nt_abort(handle, &overlap);
} else {
for (;;) {
uint32_t i;
if (g_fds.stdin.inisem) {
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
}
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
if (_check_interrupts(kSigOpRestartable)) {
goto Interrupted;
}
} else {
npassert(!i);
break;
}
}
}
ok = true;
END_BLOCKING_OPERATION;
}
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(handle, &overlap, &got, true);
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
kprintf("you complete me\n");
ok = true;
}
}
__imp_CloseHandle(overlap.hEvent);
if (nonblock) {
CancelIoEx(handle, &overlap);
} else if (_check_interrupts(kSigOpRestartable)) {
Interrupted:
pt->abort_errno = errno;
CancelIoEx(handle, &overlap);
} else {
ok = false;
for (;;) {
uint32_t i;
if (g_fds.stdin.inisem) {
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
}
i = WaitForSingleObject(overlap.hEvent, __SIG_IO_INTERVAL_MS);
if (i == kNtWaitTimeout) {
if (_check_interrupts(kSigOpRestartable)) {
goto Interrupted;
}
} else {
break;
}
}
}
} else if (offset == -1) {
// perform simple blocking file read
ok = ReadFile(handle, targetdata, targetsize, &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(handle, 0, &position, SEEK_CUR)) {
return __winerr(); // f probably isn't seekable?
pt->ioverlap = 0;
pt->iohandle = 0;
ok = true;
}
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(handle, &overlap, &got, nonblock);
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
goto BlockingOperation;
}
struct NtOverlapped overlap = {0};
overlap.Pointer = (void *)(uintptr_t)offset;
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
// restore file pointer which windows clobbers, even on error
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
}
__imp_CloseHandle(overlap.hEvent); // __imp_ to avoid log noise
if (!pwriting && seekable) {
if (ok) f->pointer = offset + got;
pthread_mutex_unlock(&f->lock);
}
if (ok) {
@ -210,7 +204,7 @@ StartOver:
case kNtErrorAccessDenied: // read doesn't return EACCESS
return ebadf(); //
case kNtErrorOperationAborted:
errno = abort_errno;
errno = pt->abort_errno;
return -1;
default:
return __winerr();
@ -225,9 +219,16 @@ textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen,
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
if (iovlen) {
for (total = i = 0; i < iovlen; ++i) {
// TODO(jart): disable cancelations after first iteration
if (!iov[i].iov_len) continue;
rc = sys_read_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset);
if (rc == -1) return -1;
if (rc == -1) {
if (total && errno != ECANCELED) {
return total;
} else {
return -1;
}
}
total += rc;
if (opt_offset != -1) opt_offset += rc;
if (rc < iov[i].iov_len) break;

View file

@ -46,9 +46,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
int64_t h;
ssize_t rc;
uint32_t mem = 16384;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
uint32_t mem = 6000;
volatile char *memory = alloca(mem);
CheckLargeStackAllocation((char *)memory, mem);
#pragma GCC pop_options
struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory;
if ((h = CreateFile(path16, kNtFileReadAttributes,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,

View file

@ -24,6 +24,7 @@
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
@ -44,24 +45,30 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) {
// translate unix to windows paths
char16_t oldpath16[PATH_MAX];
char16_t newpath16[PATH_MAX];
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 ||
__mkntpathat(newdirfd, newpath, 0, newpath16) == -1) {
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct {
char16_t oldpath16[PATH_MAX];
char16_t newpath16[PATH_MAX];
} M;
CheckLargeStackAllocation(&M, sizeof(M));
#pragma GCC pop_options
if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) == -1 ||
__mkntpathat(newdirfd, newpath, 0, M.newpath16) == -1) {
return -1;
}
// strip trailing slash
// win32 will einval otherwise
// normally this is handled by __fix_enotdir()
bool old_must_be_dir = StripTrailingSlash(oldpath16);
bool new_must_be_dir = StripTrailingSlash(newpath16);
bool old_must_be_dir = StripTrailingSlash(M.oldpath16);
bool new_must_be_dir = StripTrailingSlash(M.newpath16);
// test for some known error conditions ahead of time
// the enotdir check can't be done reactively
// ideally we should resolve symlinks first
uint32_t oldattr = __imp_GetFileAttributesW(oldpath16);
uint32_t newattr = __imp_GetFileAttributesW(newpath16);
uint32_t oldattr = __imp_GetFileAttributesW(M.oldpath16);
uint32_t newattr = __imp_GetFileAttributesW(M.newpath16);
if ((old_must_be_dir && oldattr != -1u &&
!(oldattr & kNtFileAttributeDirectory)) ||
(new_must_be_dir && newattr != -1u &&
@ -78,16 +85,16 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd,
} else if ((oldattr & kNtFileAttributeDirectory) &&
(newattr & kNtFileAttributeDirectory)) {
// both old and new are directories
if (!__imp_RemoveDirectoryW(newpath16) &&
if (!__imp_RemoveDirectoryW(M.newpath16) &&
GetLastError() == kNtErrorDirNotEmpty) {
return enotempty();
}
}
}
if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) {
if (MoveFileEx(M.oldpath16, M.newpath16, kNtMovefileReplaceExisting)) {
return 0;
} else {
return __fix_enotdir3(-1, oldpath16, newpath16);
return __fix_enotdir3(-1, M.oldpath16, M.newpath16);
}
}

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/cp.internal.h"
#include "libc/calls/struct/metatermios.internal.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -60,7 +61,9 @@ void __restore_tty(void) {
int e;
if (__isrestorable && !__isworker && !__nocolor) {
e = errno;
BEGIN_CANCELLATION_POINT;
sys_write(0, ANSI_RESTORE, __strlen(ANSI_RESTORE));
END_CANCELLATION_POINT;
tcsetattr(0, TCSAFLUSH, &__oldtermios);
errno = e;
}

475
libc/calls/sig.c Normal file
View file

@ -0,0 +1,475 @@
/*-*- 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 2022 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/sysv/consts/sig.h"
#include "ape/sections.internal.h"
#include "libc/atomic.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/popcnt.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/nt/signals.h"
#include "libc/nt/struct/context.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
struct SignalFrame {
struct PosixThread *pt;
struct NtContext *nc;
unsigned rva;
unsigned flags;
siginfo_t si;
};
struct ContextFrame {
struct SignalFrame sf;
struct NtContext nc;
};
void __stack_call(int, siginfo_t *, void *, long,
void (*)(int, siginfo_t *, void *), void *);
static textwindows bool __sig_ignored_by_default(int sig) {
return sig == SIGURG || //
sig == SIGCONT || //
sig == SIGCHLD || //
sig == SIGWINCH;
}
textwindows bool __sig_ignored(int sig) {
return __sighandrvas[sig] == (intptr_t)SIG_IGN ||
(__sighandrvas[sig] == (intptr_t)SIG_DFL &&
__sig_ignored_by_default(sig));
}
static textwindows bool __sig_should_use_altstack(unsigned flags,
struct CosmoTib *tib) {
return (flags & SA_ONSTACK) && //
tib->tib_sigstack_size && //
!(tib->tib_sigstack_flags & (SS_DISABLE | SS_ONSTACK));
}
static textwindows wontreturn void __sig_terminate(int sig) {
TerminateThisProcess(sig);
}
static textwindows bool __sig_start(struct PosixThread *pt, int sig,
unsigned *rva, unsigned *flags) {
*rva = __sighandrvas[sig];
*flags = __sighandflags[sig];
if (*rva == (intptr_t)SIG_IGN ||
(*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
STRACE("ignoring %G", sig);
return false;
}
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) {
STRACE("tid %d masked %G delivering to tib_sigpending", _pthread_tid(pt),
sig);
pt->tib->tib_sigpending |= 1ull << (sig - 1);
return false;
}
if (*rva == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
if (*flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
return true;
}
static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx,
unsigned rva, unsigned flags,
struct CosmoTib *tib) {
sigaction_f handler;
handler = (sigaction_f)(__executable_start + rva);
++__sig.count;
if (__sig_should_use_altstack(flags, tib)) {
tib->tib_sigstack_flags |= SS_ONSTACK;
__stack_call(sig, si, ctx, 0, handler,
tib->tib_sigstack_addr + tib->tib_sigstack_size);
tib->tib_sigstack_flags &= ~SS_ONSTACK;
} else {
handler(sig, si, ctx);
}
}
textwindows int __sig_raise(int sig, int sic) {
unsigned rva, flags;
struct CosmoTib *tib = __get_tls();
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
ucontext_t ctx = {.uc_sigmask.__bits[0] = tib->tib_sigmask};
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
siginfo_t si = {.si_signo = sig, .si_code = sic};
struct NtContext nc;
nc.ContextFlags = kNtContextAll;
GetThreadContext(GetCurrentThread(), &nc);
_ntcontext2linux(&ctx, &nc);
STRACE("raising %G", sig);
if (!(flags & SA_NODEFER)) {
tib->tib_sigmask |= 1ull << (sig - 1);
}
__sig_call(sig, &si, &ctx, rva, flags, tib);
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
return (flags & SA_RESTART) ? 2 : 1;
}
textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
atomic_int *futex;
if (_weaken(WakeByAddressSingle) &&
(futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) {
_weaken(WakeByAddressSingle)(futex);
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
pt->abort_errno = EINTR;
if (!CancelIoEx(pt->iohandle, pt->ioverlap)) {
int err = GetLastError();
if (err != kNtErrorNotFound) {
STRACE("CancelIoEx() failed w/ %d", err);
}
}
} else if (pt->pt_flags & PT_INSEMAPHORE) {
pt->abort_errno = EINTR;
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
}
}
}
static textwindows wontreturn void __sig_panic(const char *msg) {
#ifndef TINY
char s[128], *p = s;
p = stpcpy(p, "sig panic: ");
p = stpcpy(p, msg);
p = stpcpy(p, " failed w/ ");
p = FormatInt32(p, GetLastError());
*p++ = '\n';
WriteFile(GetStdHandle(kNtStdErrorHandle), s, p - s, 0, 0);
#endif
TerminateThisProcess(SIGVTALRM);
}
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
ucontext_t ctx = {0};
sigaction_f handler = (sigaction_f)(__executable_start + sf->rva);
STRACE("__sig_tramp(%G, %t)", sf->si.si_signo, handler);
_ntcontext2linux(&ctx, sf->nc);
ctx.uc_sigmask.__bits[0] = sf->pt->tib->tib_sigmask;
if (!(sf->flags & SA_NODEFER)) {
sf->pt->tib->tib_sigmask |= 1ull << (sf->si.si_signo - 1);
}
++__sig.count;
handler(sf->si.si_signo, &sf->si, &ctx);
sf->pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
_ntlinux2context(sf->nc, &ctx);
SetThreadContext(GetCurrentThread(), sf->nc);
__sig_panic("SetThreadContext(GetCurrentThread)");
}
static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
uintptr_t th;
unsigned rva, flags;
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
th = _pthread_syshand(pt);
uint32_t old_suspend_count;
old_suspend_count = SuspendThread(th);
if (old_suspend_count == -1u) {
STRACE("SuspendThread failed w/ %d", GetLastError());
return ESRCH;
}
if (old_suspend_count) {
STRACE("kill contention of %u on tid %d", old_suspend_count,
_pthread_tid(pt));
pt->tib->tib_sigpending |= 1ull << (sig - 1);
return 0;
}
struct NtContext nc;
nc.ContextFlags = kNtContextAll;
if (!GetThreadContext(th, &nc)) {
STRACE("GetThreadContext failed w/ %d", GetLastError());
return ESRCH;
}
uintptr_t sp;
if (__sig_should_use_altstack(flags, pt->tib)) {
pt->tib->tib_sigstack_flags |= SS_ONSTACK;
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
pt->tib->tib_sigstack_flags &= ~SS_ONSTACK;
} else {
sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16;
}
struct ContextFrame *cf = (struct ContextFrame *)sp;
bzero(&cf->sf.si, sizeof(cf->sf.si));
memcpy(&cf->nc, &nc, sizeof(nc));
cf->sf.pt = pt;
cf->sf.rva = rva;
cf->sf.nc = &cf->nc;
cf->sf.flags = flags;
cf->sf.si.si_code = sic;
cf->sf.si.si_signo = sig;
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip;
nc.Rip = (intptr_t)__sig_tramp;
nc.Rdi = (intptr_t)&cf->sf;
nc.Rsp = sp;
if (!SetThreadContext(th, &nc)) {
STRACE("SetThreadContext failed w/ %d", GetLastError());
return ESRCH;
}
ResumeThread(th); // doesn't actually resume if doing blocking i/o
__sig_cancel(pt, flags); // we can wake it up immediately by canceling it
return 0;
}
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
int rc;
BLOCK_SIGNALS;
BLOCK_CANCELLATIONS;
rc = __sig_killer(pt, sig, sic);
ALLOW_CANCELLATIONS;
ALLOW_SIGNALS;
return rc;
}
textwindows void __sig_generate(int sig, int sic) {
struct Dll *e;
struct PosixThread *pt, *mark = 0;
if (__sig_ignored(sig)) {
STRACE("ignoring %G", sig);
return;
}
if (__sighandrvas[sig] == (intptr_t)SIG_DFL) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
pt = POSIXTHREAD_CONTAINER(e);
if (atomic_load_explicit(&pt->status, memory_order_acquire) <
kPosixThreadTerminated &&
!(pt->tib->tib_sigmask & (1ull << (sig - 1)))) {
mark = pt;
break;
}
}
pthread_spin_unlock(&_pthread_lock);
if (mark) {
__sig_kill(mark, sig, sic);
} else {
STRACE("all threads block %G so adding to pending signals of process", sig);
__sig.pending |= sig;
}
}
static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint:
*code = TRAP_BRKPT;
return SIGTRAP;
case kNtSignalIllegalInstruction:
*code = ILL_ILLOPC;
return SIGILL;
case kNtSignalPrivInstruction:
*code = ILL_PRVOPC;
return SIGILL;
case kNtSignalGuardPage:
case kNtSignalInPageError:
case kNtStatusStackOverflow:
*code = SEGV_MAPERR;
return SIGSEGV;
case kNtSignalAccessViolation:
*code = SEGV_ACCERR;
return SIGSEGV;
case kNtSignalInvalidHandle:
case kNtSignalInvalidParameter:
case kNtSignalAssertionFailure:
*code = SI_USER;
return SIGABRT;
case kNtStatusIntegerOverflow:
*code = FPE_INTOVF;
return SIGFPE;
case kNtSignalFltDivideByZero:
*code = FPE_FLTDIV;
return SIGFPE;
case kNtSignalFltOverflow:
*code = FPE_FLTOVF;
return SIGFPE;
case kNtSignalFltUnderflow:
*code = FPE_FLTUND;
return SIGFPE;
case kNtSignalFltInexactResult:
*code = FPE_FLTRES;
return SIGFPE;
case kNtSignalFltDenormalOperand:
case kNtSignalFltInvalidOperation:
case kNtSignalFltStackCheck:
case kNtSignalIntegerDivideByZero:
case kNtSignalFloatMultipleFaults:
case kNtSignalFloatMultipleTraps:
*code = FPE_FLTINV;
return SIGFPE;
case kNtSignalDllNotFound:
case kNtSignalOrdinalNotFound:
case kNtSignalEntrypointNotFound:
case kNtSignalDllInitFailed:
*code = SI_KERNEL;
return SIGSYS;
default:
*code = ep->ExceptionRecord->ExceptionCode;
return SIGSEGV;
}
}
// abashed the devil stood, and felt how awful goodness is
__msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
int code, sig = __sig_crash_sig(ep, &code);
STRACE("win32 vectored exception 0x%08Xu raising %G "
"cosmoaddr2line %s %lx %s",
ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name,
ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
if (sig == SIGTRAP) {
ep->ContextRecord->Rip++;
if (__sig_ignored(sig)) {
return kNtExceptionContinueExecution;
}
}
struct PosixThread *pt = _pthread_self();
siginfo_t si = {.si_signo = sig,
.si_code = code,
.si_addr = ep->ExceptionRecord->ExceptionAddress};
unsigned rva = __sighandrvas[sig];
unsigned flags = __sighandflags[sig];
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
#ifndef TINY
intptr_t hStderr;
char sigbuf[21], s[128], *p;
hStderr = GetStdHandle(kNtStdErrorHandle);
p = stpcpy(s, "Terminating on uncaught ");
p = stpcpy(p, strsignal_r(sig, sigbuf));
p = stpcpy(p, ". Pass --strace and/or ShowCrashReports() for details.\n");
WriteFile(hStderr, s, p - s, 0, 0);
#endif
__sig_terminate(sig);
}
if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
ucontext_t ctx = {0};
_ntcontext2linux(&ctx, ep->ContextRecord);
ctx.uc_sigmask.__bits[0] = pt->tib->tib_sigmask;
__sig_call(sig, &si, &ctx, rva, flags, pt->tib);
pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
_ntlinux2context(ep->ContextRecord, &ctx);
return kNtExceptionContinueExecution;
}
static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
return SIGINT;
case kNtCtrlBreakEvent:
return SIGQUIT;
case kNtCtrlCloseEvent:
case kNtCtrlLogoffEvent: // only received by services
case kNtCtrlShutdownEvent: // only received by services
return SIGHUP;
default:
return SIGSTKFLT;
}
}
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
return true;
}
static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib,
const char *thing, int id) {
bool handler_was_called = false;
uint64_t pending, masked, deliverable;
pending = atomic_load_explicit(sigs, memory_order_acquire);
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
deliverable = pending & ~masked;
POLLTRACE("%s %d blocks %d sigs w/ %d pending and %d deliverable", thing, id,
popcnt(masked), popcnt(pending), popcnt(deliverable));
if (deliverable) {
for (int sig = 1; sig <= 64; ++sig) {
if ((deliverable & (1ull << (sig - 1))) &&
atomic_fetch_and(sigs, ~(1ull << (sig - 1))) & (1ull << (sig - 1))) {
handler_was_called |= __sig_raise(sig, SI_KERNEL);
}
}
}
return handler_was_called;
}
// returns 0 if no signal handlers were called, otherwise a bitmask
// consisting of `1` which means a signal handler was invoked which
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
// handlers were called (or `3` if both were the case).
textwindows int __sig_check(void) {
bool handler_was_called = false;
struct CosmoTib *tib = __get_tls();
handler_was_called |=
__sig_checkem(&tib->tib_sigpending, tib, "tid", tib->tib_tid);
handler_was_called |= __sig_checkem(&__sig.pending, tib, "pid", getpid());
POLLTRACE("__sig_check() → %hhhd", handler_was_called);
return handler_was_called;
}
textstartup void __sig_init(void) {
if (!IsWindows()) return;
AddVectoredExceptionHandler(true, (void *)__sig_crash);
SetConsoleCtrlHandler((void *)__sig_console, true);
}
const void *const __sig_ctor[] initarray = {__sig_init};
#endif /* __x86_64__ */

View file

@ -1,39 +1,34 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#include "libc/calls/struct/sigset.h"
#include "libc/calls/ucontext.h"
#include "libc/nt/struct/context.h"
#include "libc/thread/posixthread.internal.h"
#define __SIG_QUEUE_LENGTH 32
#define __SIG_POLLING_INTERVAL_MS 20
#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */
#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */
#define __SIG_PROC_INTERVAL_MS 1000 /* process waiting also pretty good */
#define __SIG_IO_INTERVAL_MS 1000 /* read/write cancel/notify is good */
#define __SIG_POLL_INTERVAL_MS 20 /* poll on windows is dumpster fire */
#define __SIG_LOGGING_INTERVAL_MS 1700
#define __SIG_QUEUE_LENGTH 32
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Delivery {
int ops;
int sig;
int sic;
struct NtContext *nc;
};
struct Signals {
uint64_t mask;
uint64_t pending;
uint64_t count;
_Atomic(uint64_t) pending;
_Atomic(uint64_t) count;
};
extern struct Signals __sig;
bool __sig_check(int);
bool __sig_is_core(int);
bool __sig_is_ignored(int);
bool __sig_handle(int, int, int, ucontext_t *);
bool __sig_ignored(int);
int __sig_check(void);
int __sig_kill(struct PosixThread *, int, int);
int __sig_mask(int, const sigset_t *, sigset_t *);
bool __sig_deliver(int, int, int, ucontext_t *);
int __sig_tramp(struct Delivery *);
bool32 __sig_notify(int, int);
int __sig_raise(int, int);
void __sig_cancel(struct PosixThread *, unsigned);
void __sig_generate(int, int);
void __sig_init(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -49,13 +49,8 @@
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#ifdef SYSDEBUG
__static_yoink("strsignal"); // for kprintf()
#endif
#if SupportsWindows()
__static_yoink("_init_onntconsoleevent");
__static_yoink("_init_wincrash");
__static_yoink("__sig_ctor");
#endif
#define SA_RESTORER 0x04000000
@ -250,23 +245,21 @@ static int __sigaction(int sig, const struct sigaction *act,
} else {
if (oldact) {
bzero(oldact, sizeof(*oldact));
}
rc = 0;
}
if (rc != -1 && !__vforked) {
if (oldact) {
oldrva = __sighandrvas[sig];
oldact->sa_flags = __sighandflags[sig];
oldact->sa_sigaction =
(sigaction_f)(oldrva < kSigactionMinRva
? oldrva
: (intptr_t)&__executable_start + oldrva);
}
rc = 0;
}
if (rc != -1 && !__vforked) {
if (act) {
__sighandrvas[sig] = rva;
__sighandflags[sig] = act->sa_flags;
if (IsWindows()) {
if (rva == (intptr_t)SIG_IGN ||
(rva == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) {
if (__sig_ignored(sig)) {
__sig.pending &= ~(1ull << (sig - 1));
if (__tls_enabled) {
__get_tls()->tib_sigpending &= ~(1ull << (sig - 1));
@ -281,7 +274,9 @@ static int __sigaction(int sig, const struct sigaction *act,
/**
* Installs handler for kernel interrupt to thread, e.g.:
*
* void GotCtrlC(int sig, siginfo_t *si, void *ctx);
* void GotCtrlC(int sig, siginfo_t *si, void *arg) {
* ucontext_t *ctx = arg;
* }
* struct sigaction sa = {.sa_sigaction = GotCtrlC,
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));

View file

@ -24,8 +24,10 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/ss.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
static void sigaltstack2bsd(struct sigaltstack_bsd *bsd,
const struct sigaltstack *linux) {
@ -53,14 +55,63 @@ static void sigaltstack2linux(struct sigaltstack *linux,
linux->ss_size = size;
}
static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu,
struct sigaltstack *old) {
struct CosmoTib *tib;
tib = __get_tls();
if (old) {
old->ss_sp = tib->tib_sigstack_addr;
old->ss_size = tib->tib_sigstack_size;
old->ss_flags = tib->tib_sigstack_flags;
}
if (neu) {
tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16);
tib->tib_sigstack_size = ROUNDDOWN(neu->ss_size, 16);
tib->tib_sigstack_flags &= SS_ONSTACK;
tib->tib_sigstack_flags |= neu->ss_flags & SS_DISABLE;
}
return 0;
}
static int sigaltstack_sysv(const struct sigaltstack *neu,
struct sigaltstack *old) {
void *b;
const void *a;
struct sigaltstack_bsd bsd;
if (IsLinux()) {
a = neu;
b = old;
} else {
if (neu) {
sigaltstack2bsd(&bsd, neu);
a = &bsd;
} else {
a = 0;
}
if (old) {
b = &bsd;
} else {
b = 0;
}
}
if (!sys_sigaltstack(a, b)) {
if (IsBsd() && old) {
sigaltstack2linux(old, &bsd);
}
return 0;
} else {
return -1;
}
}
/**
* Sets and/or gets alternate signal stack, e.g.
*
* struct sigaction sa;
* struct sigaltstack ss;
* ss.ss_flags = 0;
* ss.ss_sp = NewCosmoStack();
* ss.ss_size = GetStackSize();
* ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192;
* ss.ss_sp = malloc(ss.ss_size);
* sigaltstack(&ss, 0);
* sigemptyset(&sa.ss_mask);
* sa.sa_flags = SA_ONSTACK;
@ -75,41 +126,20 @@ static void sigaltstack2linux(struct sigaltstack *linux,
*/
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
int rc;
void *b;
const void *a;
struct sigaltstack_bsd bsd;
if (IsAsan() && ((old && !__asan_is_valid(old, sizeof(*old))) ||
(neu && !__asan_is_valid(neu, sizeof(*neu))))) {
rc = efault();
} else if (neu && neu->ss_size < MINSIGSTKSZ) {
} else if (neu && ((neu->ss_size >> 32) || //
(neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) {
return einval();
} else if (neu && neu->ss_size < __get_minsigstksz()) {
rc = enomem();
} else if (IsLinux() || IsBsd()) {
if (IsLinux()) {
a = neu;
b = old;
} else {
if (neu) {
sigaltstack2bsd(&bsd, neu);
a = &bsd;
} else {
a = 0;
}
if (old) {
b = &bsd;
} else {
b = 0;
}
}
if ((rc = sys_sigaltstack(a, b)) != -1) {
if (IsBsd() && old) {
sigaltstack2linux(old, &bsd);
}
rc = 0;
} else {
rc = -1;
if (!(rc = sigaltstack_sysv(neu, old))) {
sigaltstack_cosmo(neu, old);
}
} else {
rc = enosys();
rc = sigaltstack_cosmo(neu, old);
}
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstk(0, neu),
DescribeSigaltstk(0, old), rc);

View file

@ -17,11 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/log/libfatal.internal.h"
#include "libc/str/str.h"
dontasan sigset_t _sigblockall(void) {
sigset_t ss;
__repstosb(&ss, -1, sizeof(ss));
sigset_t _sigblockall(void) {
sigset_t ss = {{-1, -1}};
return _sigsetmask(ss);
}

View file

@ -1,80 +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 2022 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/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#include "libc/thread/tls2.internal.h"
#ifdef __x86_64__
intptr_t __sigchld_thread;
static atomic_uint __sigchld_once;
static struct CosmoTib __sigchld_tls;
static textwindows bool __sigchld_check(void) {
bool should_signal = false;
for (;;) {
int pids[64];
int64_t handles[64];
uint32_t n = __sample_pids(pids, handles, true);
if (!n) return should_signal;
uint32_t i = WaitForMultipleObjects(n, handles, false, 0);
if (i == kNtWaitFailed) return should_signal;
if (i == kNtWaitTimeout) return should_signal;
i &= ~kNtWaitAbandoned;
if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) &&
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) {
CloseHandle(handles[i]);
__releasefd(pids[i]);
} else {
g_fds.p[pids[i]].zombie = true;
}
should_signal = true;
}
}
static textwindows uint32_t __sigchld_worker(void *arg) {
__set_tls_win32(&__sigchld_tls);
for (;;) {
if (__sigchld_check()) {
__sig_notify(SIGCHLD, CLD_EXITED);
}
SleepEx(100, false);
}
return 0;
}
static textwindows void __sigchld_init(void) {
__sigchld_thread = CreateThread(0, 65536, __sigchld_worker, 0, 0, 0);
}
void _init_sigchld(void) {
cosmo_once(&__sigchld_once, __sigchld_init);
}
#endif /* __x86_64__ */

View file

@ -29,6 +29,7 @@
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
@ -36,11 +37,15 @@
privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
struct ucontext_freebsd *ctx) {
int rva, flags;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct Goodies {
ucontext_t uc;
siginfo_t si;
} g;
CheckLargeStackAllocation(&g, sizeof(g));
#pragma GCC pop_options
int rva, flags;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig];

View file

@ -32,7 +32,7 @@
#ifdef __x86_64__
privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
int i, rva, flags;
int rva, flags;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig];
@ -40,10 +40,6 @@ privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
// https://github.com/microsoft/WSL/issues/2555
if ((flags & SA_SIGINFO) && UNLIKELY(!ctx->uc_mcontext.fpregs)) {
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
for (i = 0; i < 8; ++i) {
long double nan = NAN;
__memcpy(ctx->__fpustate.st + i, &nan, 16);
}
}
((sigaction_f)(__executable_start + rva))(sig, info, ctx);
}

View file

@ -29,6 +29,7 @@
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
@ -36,8 +37,12 @@
privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si,
struct ucontext_netbsd *ctx) {
int rva, flags;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
ucontext_t uc;
CheckLargeStackAllocation(&uc, sizeof(uc));
#pragma GCC pop_options
int rva, flags;
struct siginfo si2;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {

View file

@ -29,6 +29,7 @@
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
@ -36,11 +37,15 @@
privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
struct ucontext_openbsd *ctx) {
int rva, flags;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct Goodies {
ucontext_t uc;
struct siginfo si;
} g;
CheckLargeStackAllocation(&g, sizeof(g));
#pragma GCC pop_options
int rva, flags;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig];

View file

@ -29,6 +29,7 @@
#include "libc/intrin/repstosb.h"
#include "libc/log/libfatal.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
@ -488,11 +489,15 @@ static privileged void linuxssefpustate2xnu(
privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
struct siginfo_xnu *xnuinfo,
struct __darwin_ucontext *xnuctx) {
int rva, flags;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct Goodies {
ucontext_t uc;
siginfo_t si;
} g;
CheckLargeStackAllocation(&g, sizeof(g));
#pragma GCC pop_options
int rva, flags;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig];

View file

@ -98,6 +98,8 @@ privileged void __siginfo2cosmo(struct siginfo *si,
si->si_addr = si_addr;
} else if (si_signo == SIGCHLD) {
si->si_status = si_status;
si->si_pid = si_pid;
si->si_uid = si_uid;
} else if (si_signo == SIGALRM) {
si->si_timerid = si_timerid;
si->si_overrun = si_overrun;

View file

@ -19,6 +19,8 @@
#include "libc/assert.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
@ -28,37 +30,43 @@
#define GetSigBit(x) (1ull << (((x)-1) & 63))
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
uint64_t x, y, *mask;
if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
if (__tls_enabled) {
mask = &__get_tls()->tib_sigmask;
} else {
mask = &__sig.mask;
}
if (old) {
old->__bits[0] = *mask;
old->__bits[1] = 0;
}
if (neu) {
x = *mask;
y = neu->__bits[0];
if (how == SIG_BLOCK) {
x |= y;
} else if (how == SIG_UNBLOCK) {
x &= ~y;
} else {
x = y;
}
x &= ~(0
#define M(x) | GetSigBit(x)
#include "libc/intrin/sigisprecious.inc"
);
*mask = x;
}
return 0;
} else {
// validate api usage
if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) {
return einval();
}
// get address of sigset to modify
_Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask;
// these signals are precious to cosmopolitan
uint64_t precious = 0
#define M(x) | GetSigBit(x)
#include "libc/intrin/sigisprecious.inc"
;
// handle read-only case
uint64_t oldmask;
if (neu) {
uint64_t input = neu->__bits[0] & ~precious;
if (how == SIG_BLOCK) {
oldmask = atomic_fetch_or_explicit(mask, input, memory_order_acq_rel);
} else if (how == SIG_UNBLOCK) {
oldmask = atomic_fetch_and_explicit(mask, input, memory_order_acq_rel);
} else { // SIG_SETMASK
oldmask = atomic_exchange_explicit(mask, input, memory_order_acq_rel);
}
} else {
oldmask = atomic_load_explicit(mask, memory_order_acquire);
}
// return old signal mask to caller
if (old) {
old->__bits[0] = oldmask;
old->__bits[1] = 0;
}
return 0;
}
#endif /* __x86_64__ */

View file

@ -60,7 +60,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
} else if (IsMetal() || IsWindows()) {
rc = __sig_mask(how, opt_set, &old);
if (_weaken(__sig_check)) {
_weaken(__sig_check)(kSigOpRestartable);
_weaken(__sig_check)();
}
} else {
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);

View file

@ -16,19 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/sig.h"
dontasan sigset_t _sigsetmask(sigset_t neu) {
sigset_t _sigsetmask(sigset_t neu) {
sigset_t res;
if (IsMetal() || IsWindows()) {
__sig_mask(SIG_SETMASK, &neu, &res);
} else {
npassert(!sys_sigprocmask(SIG_SETMASK, &neu, &res));
sys_sigprocmask(SIG_SETMASK, &neu, &res);
}
return res;
}

View file

@ -16,19 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/bo.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/log/backtrace.internal.h"
#include "libc/nt/errors.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
@ -50,7 +46,6 @@ int sigsuspend(const sigset_t *ignore) {
int rc;
const sigset_t *arg;
sigset_t save, mask = {0};
STRACE("sigsuspend(%s) → ...", DescribeSigset(0, ignore));
BEGIN_CANCELLATION_POINT;
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
@ -74,36 +69,26 @@ int sigsuspend(const sigset_t *ignore) {
rc = sys_sigsuspend(arg, 8);
} else {
__sig_mask(SIG_SETMASK, arg, &save);
#if defined(SYSDEBUG) && _POLLTRACE
long ms = 0;
long totoms = 0;
#endif
BEGIN_BLOCKING_OPERATION;
do {
if ((rc = _check_interrupts(0))) {
while (!(rc = _check_interrupts(0))) {
struct PosixThread *pt;
pt = _pthread_self();
pt->abort_errno = 0;
pt->pt_flags |= PT_INSEMAPHORE;
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
pt->pt_flags &= ~PT_INSEMAPHORE;
if (pt->abort_errno) {
errno = pt->abort_errno;
rc = -1;
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
POLLTRACE("IOCP EINTR");
continue;
}
#if defined(SYSDEBUG) && _POLLTRACE
ms += __SIG_POLLING_INTERVAL_MS;
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
totoms += ms, ms = 0;
POLLTRACE("... sigsuspending for %'lums...", totoms);
}
#endif
} while (1);
END_BLOCKING_OPERATION;
}
__sig_mask(SIG_SETMASK, &save, 0);
}
} else {
// TODO(jart): sigsuspend metal support
rc = enosys();
}
END_CANCELLATION_POINT;
STRACE("...sigsuspend → %d% m", rc);
STRACE("sigsuspend(%s) → %d% m", DescribeSigset(0, ignore), rc);
return rc;
}

View file

@ -19,12 +19,16 @@
#include "libc/atomic.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/cosmo.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
@ -37,6 +41,8 @@ static atomic_uint __sigwinch_once;
static struct CosmoTib __sigwinch_tls;
static textwindows unsigned __get_console_size(void) {
unsigned res = -1u;
__fds_lock();
for (int fd = 1; fd < 10; ++fd) {
intptr_t hConsoleOutput;
if (g_fds.p[fd].kind == kFdConsole) {
@ -48,20 +54,22 @@ static textwindows unsigned __get_console_size(void) {
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1;
unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1;
return (unsigned)yn << 16 | xn;
res = (unsigned)yn << 16 | xn;
break;
}
}
return -1u;
__fds_unlock();
return res;
}
static textwindows uint32_t __sigwinch_worker(void *arg) {
__set_tls_win32(&__sigwinch_tls);
static textwindows dontinstrument uint32_t __sigwinch_worker(void *arg) {
__bootstrap_tls(&__sigwinch_tls, __builtin_frame_address(0));
for (;;) {
unsigned old = __sigwinch_size;
unsigned neu = __get_console_size();
if (neu != old) {
__sigwinch_size = neu;
__sig_notify(SIGWINCH, SI_KERNEL);
__sig_generate(SIGWINCH, SI_KERNEL);
}
SleepEx(25, false);
}
@ -69,8 +77,10 @@ static textwindows uint32_t __sigwinch_worker(void *arg) {
}
static textwindows void __sigwinch_init(void) {
__enable_threads();
__sigwinch_size = __get_console_size();
__sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0, 0, 0);
__sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0,
kNtStackSizeParamIsAReservation, 0);
}
textwindows void _init_sigwinch(void) {

View file

@ -12,9 +12,9 @@ extern unsigned __sighandrvas[NSIG + 1];
extern unsigned __sighandflags[NSIG + 1];
extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_wipe(void);
void __fds_lock(void);
void __fds_unlock(void);
void __fds_funlock(void);
#define __vforked (__tls_enabled && (__get_tls()->tib_flags & TIB_FLAG_VFORKED))

View file

@ -41,8 +41,12 @@
* @cancellationpoint
*/
int statfs(const char *path, struct statfs *sf) {
int rc;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
union statfs_meta m;
CheckLargeStackAllocation(&m, sizeof(m));
#pragma GCC pop_options
int rc;
struct ZiposUri zipname;
BEGIN_CANCELLATION_POINT;

View file

@ -1,5 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_
#include "libc/nt/struct/overlapped.h"
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -25,7 +27,6 @@ COSMOPOLITAN_C_START_
struct Fd {
char kind;
bool zombie;
bool dontclose;
char buflen;
char buf[4];
@ -33,6 +34,8 @@ struct Fd {
unsigned mode;
int64_t handle;
int64_t extra;
int64_t pointer;
pthread_mutex_t lock;
};
struct StdinRelay {
@ -42,6 +45,7 @@ struct StdinRelay {
int64_t reader; /* ReadFile() use this instead */
int64_t writer; /* only used by WinStdinThread */
int64_t thread; /* handle for the stdio thread */
struct NtOverlapped overlap;
};
struct Fds {

View file

@ -64,11 +64,8 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *);
#define DescribeSigaction(rc, sa) DescribeSigaction(alloca(256), rc, sa)
void _init_onntconsoleevent(void);
void _init_wincrash(void);
void _init_sigwinch(void);
void _init_sigchld(void);
extern intptr_t __sigchld_thread;
extern intptr_t __sigwinch_thread;
COSMOPOLITAN_C_END_

View file

@ -12,21 +12,21 @@ struct siginfo_openbsd {
int32_t _pad[(128 / 4) - 3];
struct {
int32_t si_pid;
int32_t si_uid;
union {
struct {
int32_t si_uid;
union sigval si_value;
};
struct {
int64_t si_utime;
int64_t si_stime;
int32_t si_status;
int si_status;
};
};
};
struct {
void *si_addr;
int32_t si_trapno;
int si_trapno;
};
};
};

View file

@ -57,6 +57,11 @@ struct siginfo {
typedef struct siginfo siginfo_t;
#ifdef _COSMO_SOURCE
void __minicrash(int, siginfo_t *, void *);
char __is_stack_overflow(siginfo_t *, void *);
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGINFO_H_ */

View file

@ -16,7 +16,6 @@ int sigorset(sigset_t *, const sigset_t *, const sigset_t *) paramsnonnull();
int sigisemptyset(const sigset_t *) paramsnonnull() nosideeffect;
int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect;
int sigcountset(const sigset_t *) paramsnonnull() nosideeffect;
int sigisprecious(int) nosideeffect;
int sigprocmask(int, const sigset_t *, sigset_t *);
int sigsuspend(const sigset_t *);
int sigpending(sigset_t *);

View file

@ -31,6 +31,7 @@
#include "libc/nt/struct/luid.h"
#include "libc/nt/struct/tokenprivileges.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/errfuns.h"
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
@ -55,17 +56,23 @@ static textwindows void InitializeWinlink(void) {
textwindows int sys_symlinkat_nt(const char *target, int newdirfd,
const char *linkpath) {
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct {
char16_t target16[PATH_MAX];
char16_t linkpath16[PATH_MAX];
} M;
CheckLargeStackAllocation(&M, sizeof(M));
#pragma GCC pop_options
int targetlen;
uint32_t attrs, flags;
char16_t target16[PATH_MAX];
char16_t linkpath16[PATH_MAX];
// convert the paths
if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1;
if ((targetlen = __mkntpath(target, target16)) == -1) return -1;
if (__mkntpathat(newdirfd, linkpath, 0, M.linkpath16) == -1) return -1;
if ((targetlen = __mkntpath(target, M.target16)) == -1) return -1;
// determine if we need directory flag
if ((attrs = __imp_GetFileAttributesW(target16)) != -1u) {
if ((attrs = __imp_GetFileAttributesW(M.target16)) != -1u) {
if (attrs & kNtFileAttributeDirectory) {
flags = kNtSymbolicLinkFlagDirectory;
} else {
@ -75,7 +82,7 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd,
// win32 needs to know if it's a directory of a file symlink, but
// that's hard to determine if we're creating a broken symlink so
// we'll fall back to checking for trailing slash
if (targetlen && target16[targetlen - 1] == '\\') {
if (targetlen && M.target16[targetlen - 1] == '\\') {
flags = kNtSymbolicLinkFlagDirectory;
} else {
flags = 0;
@ -90,9 +97,9 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd,
}
// we can now proceed
if (CreateSymbolicLink(linkpath16, target16, flags)) {
if (CreateSymbolicLink(M.linkpath16, M.target16, flags)) {
return 0;
} else {
return __fix_enotdir(-1, linkpath16);
return __fix_enotdir(-1, M.linkpath16);
}
}

View file

@ -12,7 +12,6 @@ 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]);
int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool);
int ntaccesscheck(const char16_t *, uint32_t) paramsnonnull();
int sys_pause_nt(void);
int64_t __fix_enotdir(int64_t, char16_t *);
@ -21,7 +20,6 @@ int64_t __winerr(void) nocallback privileged;
int64_t ntreturn(uint32_t);
void *GetProcAddressModule(const char *, const char *);
void WinMainForked(void);
void _check_sigalrm(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/winsize.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
@ -35,11 +36,12 @@ textwindows int tcgetwinsize_nt(int fd, struct winsize *ws) {
if (fd == STDIN_FILENO) {
uint32_t dwMode;
// WIN32 doesn't allow GetConsoleScreenBufferInfoEx(stdin)
if (GetConsoleMode(g_fds.p[STDIN_FILENO].handle, &dwMode)) {
if (g_fds.p[STDOUT_FILENO].kind != kFdEmpty &&
GetConsoleMode(g_fds.p[STDOUT_FILENO].handle, &dwMode)) {
hConsoleOutput = g_fds.p[STDOUT_FILENO].handle;
if (GetConsoleMode(hConsoleOutput, &dwMode)) {
hConsoleOutput = g_fds.p[STDERR_FILENO].handle;
}
} else if (g_fds.p[STDERR_FILENO].kind != kFdEmpty &&
GetConsoleMode(g_fds.p[STDERR_FILENO].handle, &dwMode)) {
hConsoleOutput = g_fds.p[STDERR_FILENO].handle;
} else {
return enotty();
}

View file

@ -126,10 +126,12 @@ textwindows int __munge_terminal_input(char *p, uint32_t *n) {
}
}
if (got_vintr) {
delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0);
__sig_raise(SIGINT, SI_KERNEL);
delivered |= true;
}
if (got_vquit) {
delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0);
__sig_raise(SIGQUIT, SI_KERNEL);
delivered |= true;
}
if (*n && !j) {
if (delivered) {

View file

@ -16,7 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/runtime/runtime.h"
#define ASAN_ERROR "error: tinyprint() passed invalid memory, or missing NULL\n"
static bool tinyflush(int fd, char *buf, size_t n, ssize_t *toto) {
ssize_t rc;
@ -50,8 +56,13 @@ ssize_t tinyprint(int fd, const char *s, ...) {
va_list va;
ssize_t toto;
char buf[512];
BLOCK_CANCELLATIONS;
va_start(va, s);
for (toto = n = 0; s; s = va_arg(va, const char *)) {
if (IsAsan() && !__asan_is_valid_str(s)) {
write(2, ASAN_ERROR, sizeof(ASAN_ERROR) - 1);
abort();
}
while ((c = *s++)) {
buf[n++] = c;
if (n == sizeof(buf)) {
@ -64,5 +75,6 @@ ssize_t tinyprint(int fd, const char *s, ...) {
}
va_end(va);
tinyflush(fd, buf, n, &toto);
ALLOW_CANCELLATIONS;
return toto;
}

View file

@ -1,161 +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 2022 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/context.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/runtime/syslib.internal.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
static dontinline textwindows int __tkill_nt(int tid, int sig,
struct CosmoTib *tib) {
// validate api usage
if (tid <= 0 || !(1 <= sig && sig <= 64)) {
return einval();
}
// check if caller is killing themself
if (tid == gettid() && (!tib || tib == __get_tls())) {
__sig_handle(0, sig, SI_TKILL, 0);
return 0;
}
// check to see if this is a cosmo posix thread
struct Dll *e;
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
enum PosixThreadStatus status;
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
int rhs = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
if (rhs <= 0 || tid != rhs) continue;
if (tib && tib != pt->tib) continue;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
pthread_spin_unlock(&_pthread_lock);
if (status < kPosixThreadTerminated) {
if (pt->flags & PT_BLOCKED) {
pt->tib->tib_sigpending |= 1ull << (sig - 1);
return 0;
} else {
return _pthread_signal(pt, sig, SI_TKILL);
}
} else {
return 0;
}
}
pthread_spin_unlock(&_pthread_lock);
// otherwise try our luck hunting a win32 thread
if (!tib) {
intptr_t h;
if ((h = OpenThread(kNtThreadTerminate, false, tid))) {
if (TerminateThread(h, sig)) {
return 0;
} else {
return __winerr();
}
CloseHandle(h);
} else {
return __winerr();
}
} else {
return esrch();
}
}
static int __tkill_posix(int tid, int sig, struct CosmoTib *tib) {
// avoid lock when killing self
int me;
struct PosixThread *pt;
pt = (struct PosixThread *)__get_tls()->tib_pthread;
me = atomic_load_explicit(&__get_tls()->tib_tid, memory_order_relaxed);
if (tid == me && (!tib || tib == __get_tls())) {
return __syslib->pthread_kill(pt->next, sig);
}
// otherwise look for matching thread
struct Dll *e;
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
enum PosixThreadStatus status;
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
int rhs = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
if (rhs <= 0 || tid != rhs) continue;
if (tib && tib != pt->tib) continue;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
pthread_spin_unlock(&_pthread_lock);
if (status < kPosixThreadTerminated) {
return __syslib->pthread_kill(pt->next, sig);
} else {
return 0;
}
}
pthread_spin_unlock(&_pthread_lock);
return esrch();
}
// OpenBSD has an optional `tib` parameter for extra safety.
int __tkill(int tid, int sig, void *tib) {
int rc;
if (IsXnuSilicon()) {
return __tkill_posix(tid, sig, tib);
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_tkill(tid, sig, tib);
} else if (IsWindows()) {
rc = __tkill_nt(tid, sig, tib);
} else {
rc = enosys();
}
STRACE("tkill(%d, %G) → %d% m", tid, sig, rc);
return rc;
}
/**
* Kills thread.
*
* @param tid is thread id
* @param sig does nothing on xnu
* @return 0 on success, or -1 w/ errno
* @raise EAGAIN if `RLIMIT_SIGPENDING` was exceeded
* @raise EINVAL if `tid` or `sig` were invalid
* @raise ESRCH if no such `tid` existed
* @raise EPERM if permission was denied
* @asyncsignalsafe
*/
int tkill(int tid, int sig) {
return __tkill(tid, sig, 0);
}

View file

@ -16,7 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
@ -24,8 +26,63 @@
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static struct {
atomic_uint once;
union {
char path[PATH_MAX];
char16_t path16[PATH_MAX / 2];
};
} __tmpdir;
static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
static void __tmpdir_init(void) {
int i;
char *s;
uint32_t n;
if ((s = getenv("TMPDIR"))) {
if (*s != '/') {
if (!getcwd(__tmpdir.path, PATH_MAX)) {
goto GiveUp;
}
strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path));
}
strlcat(__tmpdir.path, s, sizeof(__tmpdir.path));
if (strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path)) <
PATH_MAX - NAME_MAX) {
return;
}
}
GiveUp:
if (IsWindows() &&
((n = GetTempPath(ARRAYLEN(__tmpdir.path16), __tmpdir.path16)) &&
n < ARRAYLEN(__tmpdir.path16))) {
// turn c:\foo\bar\ into c:/foo/bar/
for (i = 0; i < n; ++i) {
if (__tmpdir.path16[i] == '\\') {
__tmpdir.path16[i] = '/';
}
}
// turn c:/... into /c/...
if (IsAlpha(__tmpdir.path16[0]) && __tmpdir.path16[1] == ':' &&
__tmpdir.path16[2] == '/') {
__tmpdir.path16[1] = __tmpdir.path16[0];
__tmpdir.path16[0] = '/';
__tmpdir.path16[2] = '/';
}
tprecode16to8(__tmpdir.path, sizeof(__tmpdir.path), __tmpdir.path16);
return;
}
strcpy(__tmpdir.path, "/tmp/");
}
/**
* RII constant holding temporary file directory.
* Returns pretty good temporary directory.
*
* The order of precedence is:
*
@ -33,52 +90,12 @@
* - GetTempPath(), for the New Technology
* - /tmp/ to make security scene go crazy
*
* This guarantees an absolute path with a trailing slash. We also
* ensure `kTmpPath` isn't longer than `PATH_MAX-NAME_MAX`.
* This guarantees an absolute path with a trailing slash. The returned
* value points to static memory with `PATH_MAX` bytes. The string will
* be short enough that at least `NAME_MAX` bytes remain. This function
* is thread safe so long as callers don't modified the returned memory
*/
char kTmpPath[PATH_MAX];
static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
__attribute__((__constructor__)) static void kTmpPathInit(void) {
int i;
char *s;
uint32_t n;
char16_t path16[PATH_MAX];
if ((s = getenv("TMPDIR"))) {
if (*s != '/') {
if (!getcwd(kTmpPath, PATH_MAX)) {
goto GiveUp;
}
strlcat(kTmpPath, "/", sizeof(kTmpPath));
}
strlcat(kTmpPath, s, sizeof(kTmpPath));
if (strlcat(kTmpPath, "/", sizeof(kTmpPath)) < PATH_MAX - NAME_MAX) {
return;
}
}
GiveUp:
if (IsWindows() &&
((n = GetTempPath(ARRAYLEN(path16), path16)) && n < ARRAYLEN(path16))) {
// turn c:\foo\bar\ into c:/foo/bar/
for (i = 0; i < n; ++i) {
if (path16[i] == '\\') {
path16[i] = '/';
}
}
// turn c:/... into /c/...
if (IsAlpha(path16[0]) && path16[1] == ':' && path16[2] == '/') {
path16[1] = path16[0];
path16[0] = '/';
path16[2] = '/';
}
tprecode16to8(kTmpPath, sizeof(kTmpPath), path16);
return;
}
strcpy(kTmpPath, "/tmp/");
char *__get_tmpdir(void) {
cosmo_once(&__tmpdir.once, __tmpdir_init);
return __tmpdir.path;
}

View file

@ -44,8 +44,7 @@ int _mkstemp(char *, int);
*
* This creates a secure temporary file inside $TMPDIR. If it isn't
* defined, then /tmp is used on UNIX and GetTempPath() is used on the
* New Technology. This resolution of $TMPDIR happens once in a ctor,
* which is copied to the `kTmpPath` global.
* New Technology. This resolution of $TMPDIR happens once in a ctor.
*
* Once close() is called, the returned file is guaranteed to be deleted
* automatically. On UNIX the file is unlink()'d before this function
@ -81,14 +80,14 @@ int tmpfd(void) {
char path[PATH_MAX + 1];
if (IsLinux()) {
e = errno;
if ((fd = open(kTmpPath, O_RDWR | O_TMPFILE_LINUX, 0600)) != -1) {
if ((fd = open(__get_tmpdir(), O_RDWR | O_TMPFILE_LINUX, 0600)) != -1) {
return fd;
} else {
errno = e;
}
}
path[0] = 0;
strlcat(path, kTmpPath, sizeof(path));
strlcat(path, __get_tmpdir(), sizeof(path));
if (!(prog = program_invocation_short_name)) prog = "tmp";
strlcat(path, prog, sizeof(path));
strlcat(path, ".XXXXXX", sizeof(path));

View file

@ -98,7 +98,7 @@ struct ucontext {
uint8_t __unused[1024 / 8 - sizeof(sigset_t)];
struct sigcontext uc_mcontext;
#endif
};
} forcealign(16);
typedef struct ucontext ucontext_t;

View file

@ -75,15 +75,15 @@ static textwindows void GetNtName(char *name, int kind) {
}
}
static inline textwindows dontasan int GetNtMajorVersion(void) {
static inline textwindows int GetNtMajorVersion(void) {
return NtGetPeb()->OSMajorVersion;
}
static inline textwindows dontasan int GetNtMinorVersion(void) {
static inline textwindows int GetNtMinorVersion(void) {
return NtGetPeb()->OSMinorVersion;
}
static inline textwindows dontasan int GetNtBuildNumber(void) {
static inline textwindows int GetNtBuildNumber(void) {
return NtGetPeb()->OSBuildNumber;
}

View file

@ -240,10 +240,8 @@ static char *JoinPaths(char *buf, size_t size, const char *path,
}
int sys_unveil_linux(const char *path, const char *permissions) {
int rc;
const char *dir;
const char *last;
const char *next;
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct {
char lbuf[PATH_MAX];
char buf1[PATH_MAX];
@ -252,6 +250,11 @@ int sys_unveil_linux(const char *path, const char *permissions) {
char buf4[PATH_MAX];
} b;
CheckLargeStackAllocation(&b, sizeof(b));
#pragma GCC pop_options
int rc;
const char *dir;
const char *last;
const char *next;
if (!State.fd && (rc = unveil_init()) == -1) return rc;
if ((path && !permissions) || (!path && permissions)) return einval();

View file

@ -1,191 +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/assert.h"
#include "libc/calls/bo.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/processaccess.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/enum/th32cs.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/filetime.h"
#include "libc/nt/struct/iocounters.h"
#include "libc/nt/struct/processentry32.h"
#include "libc/nt/struct/processmemorycounters.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/lcg.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
static textwindows void AddProcessStats(int64_t h, struct rusage *ru) {
struct NtProcessMemoryCountersEx memcount = {
.cb = sizeof(struct NtProcessMemoryCountersEx)};
if (GetProcessMemoryInfo(h, &memcount, sizeof(memcount))) {
ru->ru_maxrss = MAX(ru->ru_maxrss, memcount.PeakWorkingSetSize / 1024);
ru->ru_majflt += memcount.PageFaultCount;
} else {
STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError());
}
struct NtFileTime createfiletime, exitfiletime;
struct NtFileTime kernelfiletime, userfiletime;
if (GetProcessTimes(h, &createfiletime, &exitfiletime, &kernelfiletime,
&userfiletime)) {
ru->ru_utime = timeval_add(
ru->ru_utime, WindowsDurationToTimeVal(ReadFileTime(userfiletime)));
ru->ru_stime = timeval_add(
ru->ru_stime, WindowsDurationToTimeVal(ReadFileTime(kernelfiletime)));
} else {
STRACE("%s failed %u", "GetProcessTimes", GetLastError());
}
struct NtIoCounters iocount;
if (GetProcessIoCounters(h, &iocount)) {
ru->ru_inblock += iocount.ReadOperationCount;
ru->ru_oublock += iocount.WriteOperationCount;
} else {
STRACE("%s failed %u", "GetProcessIoCounters", GetLastError());
}
}
static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
int options,
struct rusage *opt_out_rusage) {
int pids[64];
int64_t handle;
uint32_t i, count;
int64_t handles[64];
uint32_t dwExitCode;
if (*pid != -1 && *pid != 0) {
if (*pid < 0) {
// XXX: this is sloppy
*pid = -*pid;
}
if (!__isfdkind(*pid, kFdProcess)) {
// XXX: this is sloppy (see fork-nt.c)
if (!__isfdopen(*pid) &&
(handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation,
true, *pid))) {
if ((*pid = __reservefd_unlocked(-1)) != -1) {
g_fds.p[*pid].kind = kFdProcess;
g_fds.p[*pid].handle = handle;
g_fds.p[*pid].flags = O_CLOEXEC;
} else {
CloseHandle(handle);
return echild();
}
} else {
return echild();
}
}
handles[0] = g_fds.p[*pid].handle;
pids[0] = *pid;
count = 1;
} else {
count = __sample_pids(pids, handles, false);
if (!count) {
return echild();
}
}
// wait for one of the processes to terminate
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);
if (i == kNtWaitTimeout) {
return 0;
}
} else {
i = WaitForMultipleObjects(count, handles, false,
__SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
return -2;
}
}
if (i == kNtWaitFailed) {
notpossible;
}
// WaitForMultipleObjects can say kNtWaitAbandoned which MSDN
// describes as a "sort of" successful status which indicates
// someone else didn't free a mutex and you should check that
// persistent resources haven't been left corrupted. not sure
// what those resources would be for process objects, however
// this status has actually been observed when waiting on 'em
i &= ~kNtWaitAbandoned;
// this is where things get especially hairy. see exit() doc
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
notpossible;
}
if (dwExitCode == kNtStillActive) {
return -2;
}
if (dwExitCode == 0xc9af3d51u) {
dwExitCode = kNtStillActive;
}
// now pass along the result
if (opt_out_wstatus) {
*opt_out_wstatus = dwExitCode;
}
if (opt_out_rusage) {
bzero(opt_out_rusage, sizeof(*opt_out_rusage));
AddProcessStats(handles[i], opt_out_rusage);
}
CloseHandle(handles[i]);
__releasefd(pids[i]);
return pids[i];
}
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {
int rc;
sigset_t oldmask, mask = {0};
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
BEGIN_BLOCKING_OPERATION;
do {
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld);
if (rc == -1) break;
__fds_lock();
rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage);
__fds_unlock();
} while (rc == -2);
END_BLOCKING_OPERATION;
__sig_mask(SIG_SETMASK, &oldmask, 0);
return rc;
}
#endif /* __x86_64__ */

View file

@ -1,11 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_WAIT4_H_
#define COSMOPOLITAN_LIBC_CALLS_WAIT4_H_
#include "libc/calls/struct/rusage.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int sys_wait4_nt(int, int *, int, struct rusage *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_WAIT4_H_ */

View file

@ -1,129 +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/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#ifdef __x86_64__
// win32 calls this; we're running inside the thread that crashed
__msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
int sig, code;
switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint:
code = TRAP_BRKPT;
sig = SIGTRAP;
// Windows seems to be the only operating system that traps INT3 at
// addressof(INT3) rather than addressof(INT3)+1. So we must adjust
// RIP to prevent the same INT3 from being trapped forevermore.
ep->ContextRecord->Rip++;
break;
case kNtSignalIllegalInstruction:
code = ILL_ILLOPC;
sig = SIGILL;
break;
case kNtSignalPrivInstruction:
code = ILL_PRVOPC;
sig = SIGILL;
break;
case kNtSignalGuardPage:
case kNtSignalInPageError:
case kNtStatusStackOverflow:
code = SEGV_MAPERR;
sig = SIGSEGV;
break;
case kNtSignalAccessViolation:
code = SEGV_ACCERR;
sig = SIGSEGV;
break;
case kNtSignalInvalidHandle:
case kNtSignalInvalidParameter:
case kNtSignalAssertionFailure:
code = SI_USER;
sig = SIGABRT;
break;
case kNtStatusIntegerOverflow:
code = FPE_INTOVF;
sig = SIGFPE;
break;
case kNtSignalFltDivideByZero:
code = FPE_FLTDIV;
sig = SIGFPE;
break;
case kNtSignalFltOverflow:
code = FPE_FLTOVF;
sig = SIGFPE;
break;
case kNtSignalFltUnderflow:
code = FPE_FLTUND;
sig = SIGFPE;
break;
case kNtSignalFltInexactResult:
code = FPE_FLTRES;
sig = SIGFPE;
break;
case kNtSignalFltDenormalOperand:
case kNtSignalFltInvalidOperation:
case kNtSignalFltStackCheck:
case kNtSignalIntegerDivideByZero:
case kNtSignalFloatMultipleFaults:
case kNtSignalFloatMultipleTraps:
code = FPE_FLTINV;
sig = SIGFPE;
break;
case kNtSignalDllNotFound:
case kNtSignalOrdinalNotFound:
case kNtSignalEntrypointNotFound:
case kNtSignalDllInitFailed:
code = SI_KERNEL;
sig = SIGSYS;
break;
default:
code = ep->ExceptionRecord->ExceptionCode;
sig = SIGSEGV;
break;
}
STRACE("win32 vectored exception raising 0x%08Xu %G rip %x bt %s",
ep->ExceptionRecord->ExceptionCode, sig, ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
if (__sighandflags[sig] & SA_SIGINFO) {
struct Delivery pkg = {kSigOpUnmaskable, sig, code, ep->ContextRecord};
__sig_tramp(&pkg);
} else {
__sig_handle(kSigOpUnmaskable, sig, code, 0);
}
return kNtExceptionContinueExecution;
}
#endif /* __x86_64__ */

Some files were not shown because too many files have changed in this diff Show more