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,41 +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/struct/itimerval.h"
#include "libc/calls/struct/timeval.h"
#include "libc/sysv/consts/itimer.h"
/**
* Asks for single-shot SIGALRM to be raise()'d after interval.
*
* @param seconds is how long to wait before raising SIGALRM (which will
* only happen once) or zero to clear any previously scheduled alarm
* @return seconds that were remaining on the previously scheduled
* alarm, or zero if there wasn't one (failure isn't possible)
* @see setitimer() for a more powerful api
* @asyncsignalsafe
*/
unsigned alarm(unsigned seconds) {
struct itimerval it, old;
it.it_value = timeval_fromseconds(seconds);
it.it_interval = timeval_zero;
npassert(!setitimer(ITIMER_REAL, &it, &old));
return timeval_toseconds(old.it_value);
}

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

@ -1,52 +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/mem/alloca.h"
#include "libc/runtime/runtime.h"
/**
* Executes program, with current environment.
*
* The current process is replaced with the executed one.
*
* @param prog will not be PATH searched, see execlp()
* @param arg[0] is the name of the program to run
* @param arg[1,n-2] optionally specify program arguments
* @param arg[n-1] is NULL
* @return doesn't return on success, otherwise -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execl(const char *exe, const char *arg, ... /*, NULL*/) {
int i;
char **argv;
va_list va, vb;
va_copy(vb, va);
va_start(va, arg);
for (i = 0; va_arg(va, const char *); ++i) donothing;
va_end(va);
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = (char *)arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, char *))) break;
}
va_end(vb);
return execv(exe, argv);
}

View file

@ -1,54 +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/mem/alloca.h"
/**
* Executes program, with custom environment.
*
* The current process is replaced with the executed one.
*
* @param prog will not be PATH searched, see commandv()
* @param arg[0] is the name of the program to run
* @param arg[1,n-3] optionally specify program arguments
* @param arg[n-2] is NULL
* @param arg[n-1] is a pointer to a ["key=val",...,NULL] array
* @return doesn't return on success, otherwise -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execle(const char *exe, const char *arg,
... /*, NULL, char *const envp[] */) {
int i;
va_list va, vb;
char **argv, **envp;
va_copy(vb, va);
va_start(va, arg);
for (i = 0; va_arg(va, const char *); ++i) donothing;
envp = va_arg(va, char **);
va_end(va);
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = (char *)arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, char *))) break;
}
va_end(vb);
return execve(exe, argv, envp);
}

View file

@ -1,77 +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/limits.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
* Executes program, with PATH search and current environment.
*
* The current process is replaced with the executed one.
*
* @param prog is program to launch (may be PATH searched)
* @param arg[0] is the name of the program to run
* @param arg[1,n-2] optionally specify program arguments
* @param arg[n-1] is NULL
* @return doesn't return on success, otherwise -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
int i;
char *exe;
char **argv;
va_list va, vb;
char pathbuf[PATH_MAX];
// turn varargs into array
va_copy(vb, va);
va_start(va, arg);
for (i = 0; va_arg(va, const char *); ++i) (void)0;
va_end(va);
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = (char *)arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, char *))) break;
}
va_end(vb);
if (strchr(prog, '/')) {
return execv(prog, argv);
}
// resolve path of executable
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
return -1;
}
// change argv[0] to resolved path if it's ambiguous
// otherwise the program won't have much luck finding itself
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
argv[0] = exe;
}
// execute program
// tail call shouldn't be possible
return execv(exe, argv);
}

View file

@ -1,29 +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/runtime/runtime.h"
/**
* Replaces process with specific program, using default environment.
* @asyncsignalsafe
* @vforksafe
*/
int execv(const char *exe, char *const argv[]) {
return execve(exe, argv, environ);
}

View file

@ -1,284 +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/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigaction.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/nt/accounting.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/enum/threadaccess.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sock/sock.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#ifdef __x86_64__
#define keywords textwindows dontasan dontubsan dontinstrument
// clang-format off
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
__msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent;
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
__msabi extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler;
__msabi extern typeof(SetHandleInformation) *const __imp_SetHandleInformation;
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
// clang-format on
extern long __klog_handle;
static void sys_execve_nt_relay(intptr_t, long, long, long);
wontreturn void __switch_stacks(intptr_t, long, long, long,
void (*)(intptr_t, intptr_t, long, long),
intptr_t);
static keywords void PurgeHandle(intptr_t h) {
if (!h) return;
if (h == -1) return;
__imp_CloseHandle(h);
}
static keywords void PurgeThread(intptr_t h) {
if (h && h != -1) {
__imp_TerminateThread(h, SIGKILL);
__imp_CloseHandle(h);
}
}
static keywords void sys_execve_killer(void) {
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 tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
if (tid <= 0 || tid == __imp_GetCurrentThreadId()) continue;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
if (status >= kPosixThreadTerminated) continue;
int64_t hand;
if ((hand = __imp_OpenThread(kNtThreadTerminate, false, tid))) {
__imp_TerminateThread(hand, SIGKILL);
__imp_CloseHandle(hand);
}
}
pthread_spin_unlock(&_pthread_lock);
}
keywords int sys_execve_nt(const char *program, char *const argv[],
char *const envp[]) {
size_t i;
// validate api usage
if (strlen(program) + 4 < PATH_MAX) {
char progbuf[PATH_MAX];
char *end = stpcpy(progbuf, program);
char suffixes[][5] = {"", ".com", ".exe"};
for (i = 0; i < ARRAYLEN(suffixes); ++i) {
stpcpy(end, suffixes[i]);
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
break;
} else if (__imp_GetLastError() == kNtErrorSharingViolation) {
return etxtbsy(); // TODO(jart): does this work
} else {
return eacces();
}
}
} else {
return enametoolong();
}
//
// POINT OF NO RETURN
//
//
// NO! MNO!
// MNO!! [NBK] MNNOO!
// MMNO! MNNOO!!
// MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!!
// !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO!
// ! MMMMMMMMMMMMMPPPPOOOOIII! !
// MMMMMMMMMMMMPPPPPOOOOOOII!!
// MMMMMOOOOOOPPPPPPPPOOOOMII!
// MMMMM.. OPPMMP .,OMI!
// MMMM:: o.,OPMP,.o ::I!!
// NNM:::.,,OOPM!P,.::::!!
// MMNNNNNOOOOPMO!!IIPPO!!O!
// MMMMMNNNNOO:!!:!!IPPPPOO!
// MMMMMNNOOMMNNIIIPPPOO!!
// MMMONNMMNNNIIIOO!
// MN MOMMMNNNIIIIIO! OO
// MNO! IiiiiiiiiiiiI OOOO
// NNN.MNO! O!!!!!!!!!O OONO NO!
// MNNNNNO! OOOOOOOOOOO MMNNON!
// MNNNNO! PPPPPPPPP MMNON!
// OO! ON!
//
//
// kill siblings
sys_execve_killer();
PurgeThread(*_weaken(__sigchld_thread));
PurgeThread(*_weaken(__sigwinch_thread));
// pass bitmask telling child which fds are sockets
int bits;
char buf[32], *v = 0;
if (_weaken(socket)) {
for (bits = i = 0; i < 3; ++i) {
if (g_fds.p[i].kind == kFdSocket) {
bits |= 1 << i;
}
}
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
v = buf;
}
// define stdio handles for the spawned subprocess
struct NtStartupInfo si = {
.cb = sizeof(struct NtStartupInfo),
.dwFlags = kNtStartfUsestdhandles,
};
for (i = 0; i <= 2; ++i) {
if (g_fds.p[i].kind != kFdEmpty && //
!(g_fds.p[i].flags & O_CLOEXEC)) {
__imp_SetHandleInformation(g_fds.p[i].handle, kNtHandleFlagInherit, true);
si.stdiofds[i] = g_fds.p[i].handle;
} else {
si.stdiofds[i] = -1;
}
}
// launch the process
struct NtProcessInformation pi;
int rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &si, &pi);
if (rc == -1) {
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
if (__imp_GetLastError() == kNtErrorSharingViolation) {
__imp_ExitProcess(SIGVTALRM); // is ETXTBSY
} else {
__imp_ExitProcess(127 << 8);
}
}
PurgeHandle(pi.hThread);
// remove duplicate handles
for (i = 0; i <= 2; ++i) {
PurgeHandle(si.stdiofds[i]);
}
// retreat to original win32-provided stack memory
__switch_stacks(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
}
// child is in same process group so wait for it to get killed by this
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
return true; // tell win32 we handled signal
}
// this function runs on the original tiny stack that windows gave us
// we need to keep the original process alive simply to pass an int32
// so we unmap all memory to avoid getting a double whammy after fork
static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
uint32_t i, dwExitCode;
// close more handles
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
PurgeThread(g_fds.stdin.thread);
PurgeHandle(g_fds.stdin.reader);
PurgeHandle(g_fds.stdin.writer);
for (i = 0; i < g_fds.n; ++i) {
if (g_fds.p[i].kind != kFdEmpty) {
PurgeHandle(g_fds.p[i].handle);
if (g_fds.p[i].kind == kFdConsole) {
PurgeHandle(g_fds.p[i].extra);
}
}
}
if (_weaken(__klog_handle)) {
PurgeHandle(*_weaken(__klog_handle));
}
// free all the memory mmap created
for (i = 0; i < _mmi.i; ++i) {
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
PurgeHandle(_mmi.p[i].h);
}
// wait for process to terminate
//
// WaitForSingleObject can return 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
do {
if (__imp_WaitForSingleObject(h, -1) == kNtWaitFailed) {
notpossible;
}
if (!__imp_GetExitCodeProcess(h, &dwExitCode)) {
notpossible;
}
} while (dwExitCode == kNtStillActive);
// propagate child exit status to parent
__imp_ExitProcess(dwExitCode);
__builtin_unreachable();
}
#endif /* __x86_64__ */

View file

@ -1,141 +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 "ape/ape.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/cp.internal.h"
#include "libc/calls/execve.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/mem/alloca.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
#define ELIBBAD_LINUX 80
#define EBADEXEC_XNU 85
#define EBADARCH_XNU 86
static struct {
atomic_uint once;
const char *home;
const char *tmpdir;
} g_execve;
static bool IsApeFile(const char *path) {
if (!endswith(path, ".com")) {
return true;
} else {
bool res = false;
BLOCK_CANCELLATIONS;
BEGIN_CANCELLATION_POINT;
int fd;
char buf[8];
int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC;
if ((fd = sys_openat(AT_FDCWD, path, flags, 0)) != -1) {
res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf);
sys_close(fd);
}
END_CANCELLATION_POINT;
ALLOW_CANCELLATIONS;
return res;
}
}
static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) {
size_t n, m;
if (a && *a) {
n = strlen(a);
m = strlen(b);
if (n + m + 1 < PATH_MAX) {
stpcpy(stpcpy(buf, a), b);
return buf;
}
}
return 0;
}
static void RetryExecve(const char *prog, char **argv, char *const *envp) {
if ((argv[0] = (char *)prog)) {
STRACE("execve(%#s, %s) due to %s", prog, DescribeStringList(argv),
_strerrno(errno));
__sys_execve(prog, argv, envp);
}
}
static void SetupExecve(void) {
g_execve.home = getenv("HOME");
g_execve.tmpdir = getenv("TMPDIR");
}
__attribute__((__constructor__)) static void InitExecve(void) {
cosmo_once(&g_execve.once, SetupExecve);
}
int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
// try kernel
// this also checks execute bit
__sys_execve(prog, argv, envp);
if (!(errno == ENOEXEC || (IsLinux() && errno == ELIBBAD_LINUX))) {
return -1;
}
// allocate memory
int argc;
for (argc = 0; argv[argc];) ++argc;
char **shargs = alloca((argc + 4) * sizeof(char *));
// try ape loader
if (IsApeFile(prog)) {
shargs[1] = (char *)"-";
shargs[2] = (char *)prog;
memcpy(shargs + 3, argv, (argc + 1) * sizeof(char *));
RetryExecve("/usr/bin/ape", shargs, envp);
char *buf = alloca(PATH_MAX);
const char *name = "/.ape-" APE_VERSION_STR;
InitExecve();
RetryExecve(Join(g_execve.tmpdir, name, buf), shargs, envp);
RetryExecve(Join(g_execve.home, name, buf), shargs, envp);
RetryExecve(Join(".", name, buf), shargs, envp);
}
// try bourne shell
shargs[0] = _PATH_BSHELL;
shargs[1] = (char *)prog;
memcpy(shargs + 2, argv + 1, argc * sizeof(char *));
RetryExecve(shargs[0], shargs, envp);
enoexec();
return -1;
}

View file

@ -1,93 +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/pledge.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/libfatal.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Replaces current process with program.
*
* On Windows, `argv` and `envp` can't contain binary strings. They need
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
* corrupted.
*
* On Windows, only file descriptors 0, 1 and 2 can be passed to a child
* process in such a way that allows them to be automatically discovered
* when the child process initializes. Cosmpolitan currently treats your
* other file descriptors as implicitly O_CLOEXEC.
*
* @param program will not be PATH searched, see commandv()
* @param argv[0] is the name of the program to run
* @param argv[1,n-2] optionally specify program arguments
* @param argv[n-1] is NULL
* @param envp[0,n-2] specifies "foo=bar" environment variables
* @param envp[n-1] is NULL
* @return doesn't return, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execve(const char *prog, char *const argv[], char *const envp[]) {
int rc;
struct ZiposUri uri;
if (!prog || !argv || !envp ||
(IsAsan() && (!__asan_is_valid_str(prog) || //
!__asan_is_valid_strlist(argv) || //
!__asan_is_valid_strlist(envp)))) {
rc = efault();
} else {
STRACE("execve(%#s, %s, %s)", prog, DescribeStringList(argv),
DescribeStringList(envp));
rc = 0;
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode);
}
if (!rc) {
if (0 && _weaken(__zipos_parseuri) &&
(_weaken(__zipos_parseuri)(prog, &uri) != -1)) {
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC);
if (rc != -1) {
const int zipFD = rc;
strace_enabled(-1);
rc = fexecve(zipFD, argv, envp);
close(zipFD);
strace_enabled(+1);
}
} else if (!IsWindows()) {
rc = sys_execve(prog, argv, envp);
} else {
rc = sys_execve_nt(prog, argv, envp);
}
}
}
STRACE("execve(%#s) failed %d% m", prog, rc);
return rc;
}

View file

@ -1,12 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
#define COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void __execve_lock(void);
void __execve_unlock(void);
bool IsApeLoadable(char[8]);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ */

View file

@ -1,29 +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/runtime/runtime.h"
/**
* Replaces process, with path search, using default environment.
* @asyncsignalsafe
* @vforksafe
*/
int execvp(const char *file, char *const argv[]) {
return execvpe(file, argv, environ);
}

View file

@ -1,75 +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/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/limits.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
* Executes program, with path environment search.
*
* This function is a wrapper of the execve() system call that does path
* resolution. The `PATH` environment variable is taken from your global
* `environ` rather than the `envp` argument.
*
* @param prog is the program to launch
* @param argv is [file,argv..argvₙ,NULL]
* @param envp is ["key=val",...,NULL]
* @return doesn't return on success, otherwise -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execvpe(const char *prog, char *const argv[], char *const *envp) {
size_t i;
char *exe, **argv2;
char pathbuf[PATH_MAX];
// validate memory
if (IsAsan() &&
(!__asan_is_valid_str(prog) || !__asan_is_valid_strlist(argv))) {
return efault();
}
if (strchr(prog, '/')) {
return execve(prog, argv, envp);
}
// resolve path of executable
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
return -1;
}
// change argv[0] to resolved path if it's ambiguous
// otherwise the program won't have much luck finding itself
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
for (i = 0; argv[i++];) (void)0;
argv2 = alloca(i * sizeof(*argv));
memcpy(argv2, argv, i * sizeof(*argv));
argv2[0] = exe;
argv = argv2;
}
// execute program
// tail call shouldn't be possible
return execve(exe, argv, envp);
}

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;

View file

@ -1,7 +1,7 @@
/*-*- 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
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
@ -17,20 +17,35 @@
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"
/**
* Kills thread, the Linux way.
* Returns true if file descriptor exists.
*
* @param tgid is thread group id, which on Linux means process id
* @param tid is thread id
* @raises ENOSYS on non-Linux
* @see tkill()
* This function is equivalent to:
*
* fcntl(fd, F_GETFL) != -1
*
* Except it won't clobber `errno` and has minimal linkage.
*/
int tgkill(int tgid, int tid, int sig) {
int rc;
rc = sys_tgkill(tgid, tid, sig);
STRACE("tgkill(%d, %d, %G) → %d% m", tgid, tid, sig, rc);
return rc;
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

@ -1,23 +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/state.internal.h"
#include "libc/thread/thread.h"
unsigned __sighandrvas[NSIG + 1];
unsigned __sighandflags[NSIG + 1];

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

@ -1,48 +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/struct/itimerval.h"
#include "libc/calls/struct/itimerval.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Retrieves last setitimer() value, correcting for remaining time.
*
* @param which can be ITIMER_REAL, ITIMER_VIRTUAL, etc.
* @return 0 on success or -1 w/ errno
*/
int getitimer(int which, struct itimerval *curvalue) {
int rc;
if (IsAsan() && !__asan_is_valid(curvalue, sizeof(*curvalue))) {
rc = efault();
} else if (!IsWindows()) {
rc = sys_getitimer(which, curvalue);
} else if (!curvalue) {
rc = efault();
} else {
rc = sys_setitimer_nt(which, 0, curvalue);
}
STRACE("getitimer(%s, [%s]) → %d% m", DescribeItimer(which),
DescribeItimerval(rc, curvalue), rc);
return rc;
}

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

@ -1,56 +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/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/str/str.h"
/**
* Sends signal to process.
*
* The impact of this action can be terminating the process, or
* interrupting it to request something happen.
*
* @param pid can be:
* >0 signals one process by id
* =0 signals all processes in current process group
* -1 signals all processes possible (except init)
* <-1 signals all processes in -pid process group
* @param sig can be:
* >0 can be SIGINT, SIGTERM, SIGKILL, SIGUSR1, etc.
* =0 checks both if pid exists and we can signal it
* @return 0 if something was accomplished, or -1 w/ errno
* @raise ESRCH if `pid` couldn't be found
* @raise EPERM if lacked permission to signal process
* @raise EPERM if pledge() is in play without `proc` promised
* @raise EINVAL if the provided `sig` is invalid or unsupported
* @asyncsignalsafe
*/
int kill(int pid, int sig) {
int rc;
if (!IsWindows()) {
rc = sys_kill(pid, sig, 1);
} else {
rc = sys_kill_nt(pid, sig);
}
STRACE("kill(%d, %G) → %d% m", pid, sig, rc);
return rc;
}

View file

@ -1,30 +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/dce.h"
#include "libc/sysv/errfuns.h"
/**
* Sends signal to process group.
*/
int killpg(int pgrp, int sig) {
if (!(0 < sig && sig < NSIG)) return einval();
if (pgrp == 1 || pgrp < 0) return esrch();
return kill(IsWindows() ? pgrp : -pgrp, sig);
}

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,79 +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/ucontext.h"
#include "libc/log/libfatal.internal.h"
#include "libc/nt/struct/context.h"
#include "libc/str/str.h"
#ifdef __x86_64__
textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
if (!cr) return;
ctx->uc_mcontext.eflags = cr->EFlags;
ctx->uc_mcontext.rax = cr->Rax;
ctx->uc_mcontext.rbx = cr->Rbx;
ctx->uc_mcontext.rcx = cr->Rcx;
ctx->uc_mcontext.rdx = cr->Rdx;
ctx->uc_mcontext.rdi = cr->Rdi;
ctx->uc_mcontext.rsi = cr->Rsi;
ctx->uc_mcontext.rbp = cr->Rbp;
ctx->uc_mcontext.rsp = cr->Rsp;
ctx->uc_mcontext.rip = cr->Rip;
ctx->uc_mcontext.r8 = cr->R8;
ctx->uc_mcontext.r9 = cr->R9;
ctx->uc_mcontext.r10 = cr->R10;
ctx->uc_mcontext.r11 = cr->R11;
ctx->uc_mcontext.r12 = cr->R12;
ctx->uc_mcontext.r13 = cr->R13;
ctx->uc_mcontext.r14 = cr->R14;
ctx->uc_mcontext.r15 = cr->R15;
ctx->uc_mcontext.cs = cr->SegCs;
ctx->uc_mcontext.gs = cr->SegGs;
ctx->uc_mcontext.fs = cr->SegFs;
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
__repmovsb(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate));
}
textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) {
if (!cr) return;
cr->EFlags = ctx->uc_mcontext.eflags;
cr->Rax = ctx->uc_mcontext.rax;
cr->Rbx = ctx->uc_mcontext.rbx;
cr->Rcx = ctx->uc_mcontext.rcx;
cr->Rdx = ctx->uc_mcontext.rdx;
cr->Rdi = ctx->uc_mcontext.rdi;
cr->Rsi = ctx->uc_mcontext.rsi;
cr->Rbp = ctx->uc_mcontext.rbp;
cr->Rsp = ctx->uc_mcontext.rsp;
cr->Rip = ctx->uc_mcontext.rip;
cr->R8 = ctx->uc_mcontext.r8;
cr->R9 = ctx->uc_mcontext.r9;
cr->R10 = ctx->uc_mcontext.r10;
cr->R11 = ctx->uc_mcontext.r11;
cr->R12 = ctx->uc_mcontext.r12;
cr->R13 = ctx->uc_mcontext.r13;
cr->R14 = ctx->uc_mcontext.r14;
cr->R15 = ctx->uc_mcontext.r15;
cr->SegCs = ctx->uc_mcontext.cs;
cr->SegGs = ctx->uc_mcontext.gs;
cr->SegFs = ctx->uc_mcontext.fs;
__repmovsb(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate));
}
#endif /* __x86_64__ */

View file

@ -1,111 +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/ntspawn.h"
#include "libc/assert.h"
#include "libc/calls/struct/sigaction.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/pushpop.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
struct SpawnBlock {
union {
struct {
char16_t cmdline[ARG_MAX / 2];
char16_t envvars[ARG_MAX / 2];
char buf[ARG_MAX];
};
char __pad[ROUNDUP(ARG_MAX / 2 * 3 * sizeof(char16_t), FRAMESIZE)];
};
};
/**
* Spawns process on Windows NT.
*
* This function delegates to CreateProcess() with UTF-8 UTF-16
* translation and argv escaping. Please note this will NOT escape
* command interpreter syntax.
*
* @param prog won't be PATH searched
* @param argv specifies prog arguments
* @param envp[𝟶,m-2] specifies "foo=bar" environment variables, which
* don't need to be passed in sorted order; however, this function
* goes faster the closer they are to sorted
* @param envp[m-1] is NULL
* @param extravar is added to envp to avoid setenv() in caller
* @param bInheritHandles means handles already marked inheritable will
* be inherited; which, assuming the System V wrapper functions are
* being used, should mean (1) all files and sockets that weren't
* opened with O_CLOEXEC; and (2) all memory mappings
* @param opt_out_lpProcessInformation can be used to return process and
* thread IDs to parent, as well as open handles that need close()
* @return 0 on success, or -1 w/ errno
* @see spawnve() which abstracts this function
*/
textwindows int ntspawn(
const char *prog, char *const argv[], char *const envp[],
const char *extravar,
const struct NtSecurityAttributes *opt_lpProcessAttributes,
const struct NtSecurityAttributes *opt_lpThreadAttributes,
bool32 bInheritHandles, uint32_t dwCreationFlags,
const char16_t *opt_lpCurrentDirectory,
const struct NtStartupInfo *lpStartupInfo,
struct NtProcessInformation *opt_out_lpProcessInformation) {
int rc;
int64_t handle;
struct SpawnBlock *block;
char16_t prog16[PATH_MAX];
rc = -1;
block = NULL;
_init_sigchld();
if (__mkntpath(prog, prog16) == -1) return -1;
if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0,
sizeof(*block), 0)) &&
(block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
sizeof(*block), 0)) &&
mkntcmdline(block->cmdline, argv) != -1 &&
mkntenvblock(block->envvars, envp, extravar, block->buf) != -1 &&
CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment |
kNtInheritParentAffinity,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
} else if (GetLastError() == kNtErrorSharingViolation) {
etxtbsy();
}
if (block) UnmapViewOfFile(block);
if (handle) CloseHandle(handle);
return __fix_enotdir(rc, prog16);
}
#endif /* __x86_64__ */

View file

@ -1,21 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
#define COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
#include "libc/limits.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int mkntcmdline(char16_t[ARG_MAX / 2], char *const[]);
int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *,
char[ARG_MAX]);
int ntspawn(const char *, char *const[], char *const[], const char *,
const struct NtSecurityAttributes *,
const struct NtSecurityAttributes *, bool32, uint32_t,
const char16_t *, const struct NtStartupInfo *,
struct NtProcessInformation *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_ */

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

View file

@ -1,51 +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/internal.h"
#include "libc/dce.h"
#include "libc/stdio/rand.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
/**
* Returns handles of windows pids being tracked.
*
* We return 64 at most because Windows can't await on a larger number
* of things at the same time. If we have a lot of subprocesses, then we
* choose a subgroup to monitor at random.
*
* @return number of items returned in pids and handles
*/
textwindows int __sample_pids(int pids[hasatleast 64],
int64_t handles[hasatleast 64],
bool exploratory) {
uint32_t i, j, base, count;
base = _rand64() >> 32;
for (count = i = 0; i < g_fds.n; ++i) {
j = (base + i) % g_fds.n;
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
pids[count] = j;
handles[count] = g_fds.p[j].handle;
if (++count == 64) {
break;
}
}
}
return count;
}

View file

@ -1,77 +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/sig.internal.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
static struct itimerval g_setitimer;
textwindows void _check_sigalrm(void) {
struct timeval now;
if (timeval_iszero(g_setitimer.it_value)) return;
now = timeval_real();
if (timeval_cmp(now, g_setitimer.it_value) < 0) return;
if (timeval_iszero(g_setitimer.it_interval)) {
g_setitimer.it_value = timeval_zero;
} else {
do {
g_setitimer.it_value =
timeval_add(g_setitimer.it_value, g_setitimer.it_interval);
} while (timeval_cmp(now, g_setitimer.it_value) > 0);
}
__sig.pending |= 1ull << (SIGALRM - 1);
}
textwindows void sys_setitimer_nt_reset(void) {
// this function is called by fork(), because
// timers aren't inherited by forked subprocesses
bzero(&g_setitimer, sizeof(g_setitimer));
}
textwindows int sys_setitimer_nt(int which, const struct itimerval *neu,
struct itimerval *old) {
struct itimerval config;
if (which != ITIMER_REAL || (neu && (!timeval_isvalid(neu->it_value) ||
!timeval_isvalid(neu->it_interval)))) {
return einval();
}
if (neu) {
// POSIX defines setitimer() with the restrict keyword but let's
// accommodate the usage setitimer(ITIMER_REAL, &it, &it) anyway
config = *neu;
}
if (old) {
old->it_interval = g_setitimer.it_interval;
old->it_value = timeval_subz(g_setitimer.it_value, timeval_real());
}
if (neu) {
if (!timeval_iszero(config.it_value)) {
config.it_value = timeval_add(config.it_value, timeval_real());
}
g_setitimer = config;
}
return 0;
}
#endif /* __x86_64__ */

View file

@ -1,89 +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/struct/itimerval.h"
#include "libc/calls/struct/itimerval.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
/**
* Schedules delivery of one-shot or intermittent interrupt signal, e.g.
*
* Raise SIGALRM every 1.5s:
*
* sigaction(SIGALRM,
* &(struct sigaction){.sa_handler = OnSigalrm},
* NULL);
* setitimer(ITIMER_REAL,
* &(const struct itimerval){{1, 500000},
* {1, 500000}},
* NULL);
*
* Single-shot alarm to interrupt connect() after 50ms:
*
* sigaction(SIGALRM,
* &(struct sigaction){.sa_handler = OnSigalrm,
* .sa_flags = SA_RESETHAND},
* NULL);
* setitimer(ITIMER_REAL,
* &(const struct itimerval){{0, 0}, {0, 50000}},
* NULL);
* if (connect(...) == -1 && errno == EINTR) { ... }
*
* Disarm existing timer:
*
* setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL);
*
* If the goal is to use alarms to interrupt blocking i/o routines, e.g.
* read(), connect(), etc. then it's important to install the signal
* handler using sigaction() rather than signal(), because the latter
* sets the `SA_RESTART` flag.
*
* Timers are not inherited across fork.
*
* @param which can be ITIMER_REAL, ITIMER_VIRTUAL, etc.
* @param newvalue specifies the interval ({0,0} means one-shot) and
* duration ({0,0} means disarm) in microseconds [0,999999] and
* if this parameter is NULL, we'll polyfill getitimer() behavior
* @param out_opt_old may receive remainder of previous op (if any)
* @return 0 on success or -1 w/ errno
*/
int setitimer(int which, const struct itimerval *newvalue,
struct itimerval *oldvalue) {
int rc;
if (IsAsan() &&
((newvalue && !__asan_is_valid(newvalue, sizeof(*newvalue))) ||
(oldvalue && !__asan_is_valid(oldvalue, sizeof(*oldvalue))))) {
rc = efault();
} else if (!IsWindows()) {
if (newvalue) {
rc = sys_setitimer(which, newvalue, oldvalue);
} else {
rc = sys_getitimer(which, oldvalue);
}
} else {
rc = sys_setitimer_nt(which, newvalue, oldvalue);
}
STRACE("setitimer(%s, %s, [%s]) → %d% m", DescribeItimer(which),
DescribeItimerval(0, newvalue), DescribeItimerval(rc, oldvalue), rc);
return rc;
}

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

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