Improve locks and signals

- Introduce fast spinlock API
- Double rand64() perf w/ spinlock
- Improve raise() on New Technology
- Support gettid() across platforms
- Implement SA_NODEFER on New Technology
- Move the lock intrinsics into LIBC_INTRIN
- Make SIGTRAP recoverable on New Technology
- Block SIGCHLD in wait4() on New Technology
- Add threading prototypes for XNU and FreeBSD
- Rewrite abort() fixing its minor bugs on XNU/NT
- Shave down a lot of the content in libc/bits/bits.h
- Let signal handlers modify CPU registers on New Technology
This commit is contained in:
Justine Tunney 2022-04-12 05:20:17 -07:00
parent f68f1789bd
commit 046c7ebd4a
110 changed files with 1514 additions and 876 deletions

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
@ -41,7 +41,7 @@ int atfork(void *fn, void *arg) {
for (;;) {
i = g_atfork.i;
if (i == ARRAYLEN(g_atfork.p)) return enomem();
if (cmpxchg(&g_atfork.i, i, i + 1)) {
if (_cmpxchg(&g_atfork.i, i, i + 1)) {
g_atfork.p[i] = (struct AtForkCallback){.fn = fn, .arg = arg};
return 0;
}

View file

@ -123,6 +123,7 @@ int getdomainname(char *, size_t);
int gethostname(char *, size_t);
int getpgid(int);
int getpid(void);
int gettid(void);
int getppid(void);
int getpriority(int, unsigned);
int getrlimit(int, struct rlimit *);
@ -227,7 +228,6 @@ uint32_t geteuid(void) nosideeffect;
uint32_t getgid(void) nosideeffect;
uint32_t getpgrp(void) nosideeffect;
uint32_t getsid(int) nosideeffect;
uint32_t gettid(void) nosideeffect;
uint32_t getuid(void) nosideeffect;
uint32_t umask(int32_t);
void rewinddir(DIR *);

View file

@ -17,9 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
@ -40,7 +40,7 @@ int __ensurefds(int fd) {
if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) {
memcpy(p2, p1, n1 * sizeof(*p1));
bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
if (cmpxchg(&g_fds.p, p1, p2)) {
if (_cmpxchg(&g_fds.p, p1, p2)) {
g_fds.n = n2;
if (weaken(free)) {
if (p1 == g_fds.__init_p) {

View file

@ -16,10 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/flock.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/fileflagandattributes.h"
@ -45,8 +45,8 @@ static textwindows int sys_fcntl_nt_reservefd(int start) {
if (fd >= g_fds.n) {
if (__ensurefds(fd) == -1) return -1;
}
cmpxchg(&g_fds.f, fd, fd + 1);
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
_cmpxchg(&g_fds.f, fd, fd + 1);
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
return fd;
}
}

View file

@ -18,5 +18,6 @@
*/
#include "libc/calls/internal.h"
cthread_spinlock_t __sig_lock;
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];

View file

@ -1,37 +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"
extern int __pid;
/**
* Returns process id.
* @asyncsignalsafe
* @vforksafe
*/
int getpid(void) {
int rc;
if (!__vforked) {
rc = __pid;
} else {
rc = sys_getpid().ax;
}
return rc;
}

View file

@ -15,6 +15,7 @@
#include "libc/calls/struct/winsize.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nt/struct/context.h"
@ -72,6 +73,7 @@ extern const struct Fd kEmptyFd;
hidden extern int __vforked;
hidden extern bool __time_critical;
hidden extern cthread_spinlock_t __sig_lock;
hidden extern unsigned __sighandrvas[NSIG];
hidden extern unsigned __sighandflags[NSIG];
hidden extern struct Fds g_fds;
@ -176,6 +178,7 @@ i32 sys_renameat(i32, const char *, i32, const char *) hidden;
i32 sys_sched_setaffinity(i32, u64, const void *) hidden;
i32 sys_sched_yield(void) hidden;
i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden;
i32 sys_setpgid(i32, i32) hidden;
i32 sys_setpriority(i32, u32, i32) hidden;
i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden;
i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden;
@ -330,7 +333,8 @@ struct NtOverlapped *_offset2overlap(int64_t, struct NtOverlapped *) hidden;
unsigned __wincrash_nt(struct NtExceptionPointers *);
void *GetProcAddressModule(const char *, const char *) hidden;
void WinMainForked(void) hidden;
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
void _ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
void _ntlinux2context(struct NtContext *, const ucontext_t *) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal

View file

@ -17,10 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/nt/errors.h"
#include "libc/nt/iphlpapi.h"
#include "libc/nt/runtime.h"
@ -275,7 +275,7 @@ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) {
if (!node) goto err;
if (!__hostInfo) {
__hostInfo = node;
if (cmpxchg(&once, false, true)) {
if (_cmpxchg(&once, false, true)) {
atexit(freeHostInfo);
}
}

View file

@ -17,7 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h"
#include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
@ -30,34 +30,60 @@
#include "libc/sysv/errfuns.h"
textwindows int sys_kill_nt(int pid, int sig) {
bool ok;
bool32 ok;
int64_t handle;
int event, ntpid;
if (pid) {
pid = ABS(pid);
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
/* kill(pid, SIGINT|SIGQUIT) */
if (__isfdkind(pid, kFdProcess)) {
ntpid = GetProcessId(g_fds.p[pid].handle);
} else if (!__isfdopen(pid)) {
/* XXX: this is sloppy (see fork-nt.c) */
ntpid = pid;
} else {
return esrch();
}
ok = !!GenerateConsoleCtrlEvent(event, ntpid);
} else if (__isfdkind(pid, kFdProcess)) {
ok = !!TerminateProcess(g_fds.p[pid].handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
} else if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
ok = !!TerminateProcess(handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
CloseHandle(handle);
} else {
ok = false;
}
return ok ? 0 : __winerr();
} else {
// 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()) {
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;
}
}
// XXX: Is this a cosmo pid that was returned by fork_nt?
if (__isfdkind(pid, kFdProcess)) {
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
return 0;
}
// XXX: Is this a raw new technology pid? Because that's messy.
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
ok = TerminateProcess(handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) {
ok = true; // cargo culting other codebases here
}
CloseHandle(handle);
return 0;
} else {
return -1;
}
}

View file

@ -18,7 +18,9 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/str/str.h"
/**
* Sends signal to process.
@ -38,9 +40,12 @@
* @asyncsignalsafe
*/
int kill(int pid, int sig) {
int rc;
if (!IsWindows()) {
return sys_kill(pid, sig, 1);
rc = sys_kill(pid, sig, 1);
} else {
return sys_kill_nt(pid, sig);
rc = sys_kill_nt(pid, sig);
}
STRACE("kill(%d, %s) → %d% m", pid, strsignal(sig), rc);
return rc;
}

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/bits/asmflag.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.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/bits/bits.h"
#include "libc/bits/asmflag.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/sysv/consts/map.h"

View file

@ -21,7 +21,7 @@
#include "libc/nt/struct/context.h"
#include "libc/str/str.h"
textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
if (!cr) return;
ctx->uc_flags = cr->EFlags;
ctx->uc_mcontext.gregs[REG_EFL] = cr->EFlags;
@ -48,3 +48,30 @@ textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate));
}
textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) {
if (!cr) return;
cr->EFlags = ctx->uc_flags;
cr->EFlags = ctx->uc_mcontext.gregs[REG_EFL];
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;
memcpy(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate));
}

View file

@ -17,11 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/enum/ctrlevent.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
STRACE("__onntconsoleevent(%u)", dwCtrlType);
switch (dwCtrlType) {
case kNtCtrlCEvent:
__sig_add(SIGINT, SI_KERNEL);

View file

@ -27,7 +27,7 @@
#include "libc/sysv/errfuns.h"
/**
* Waits for any signal.
* Waits for signal.
*
* This suspends execution until an unmasked signal is delivered and its
* callback function has been called. The current signal mask is used.

View file

@ -17,16 +17,25 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h"
#include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/console.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
static textwindows inline bool HasWorkingConsole(void) {
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
}
/**
* Sends signal to this process.
*
@ -36,7 +45,7 @@
*/
int raise(int sig) {
int rc, event;
STRACE("raise(%d) → [...]", sig);
STRACE("raise(%s) → [...]", strsignal(sig));
if (sig == SIGTRAP) {
DebugBreak();
rc = 0;
@ -45,18 +54,27 @@ int raise(int sig) {
x = 1 / x;
rc = 0;
} else if (!IsWindows()) {
// XXX: should be tkill() or tgkill() on linux
rc = sys_kill(getpid(), sig, 1);
} else {
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
// XXX: MSDN says "If this parameter is zero, the signal is
// generated in all processes that share the console of the
// calling process." which seems to imply multiple process
// groups potentially. We just shouldn't use this because it
// doesn't make any sense and it's so evil.
if (GenerateConsoleCtrlEvent(event, 0)) {
// XXX: we shouldn't need to sleep here ctrl-c is evil on nt
SleepEx(100, false);
__sig_check(false);
rc = 0;
} else {
rc = __winerr();
}
} else {
rc = __sig_add(sig, SI_USER);
rc = __sig_raise(sig, SI_USER);
}
}
STRACE("[...] raise → %d% m", rc);
STRACE("[...] raise(%s) → %d% m", strsignal(sig), rc);
return rc;
}

View file

@ -16,8 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/lockcmpxchg.h"
void __releasefd(int fd) {
int x;
@ -26,6 +27,6 @@ void __releasefd(int fd) {
do {
x = g_fds.f;
if (fd >= x) break;
} while (!cmpxchg(&g_fds.f, x, fd));
} while (!_lockcmpxchg(&g_fds.f, x, fd));
}
}

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/mem/mem.h"
#include "libc/sysv/errfuns.h"
@ -31,8 +31,8 @@ int __reservefd(void) {
if (fd >= g_fds.n) {
if (__ensurefds(fd) == -1) return -1;
}
cmpxchg(&g_fds.f, fd, fd + 1);
if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
_cmpxchg(&g_fds.f, fd, fd + 1);
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
return fd;
}
}

View file

@ -35,7 +35,7 @@ textwindows int __sample_pids(int pids[hasatleast 64],
bool exploratory) {
static uint64_t rando = 1;
uint32_t i, j, base, count;
base = KnuthLinearCongruentialGenerator(&rando);
base = KnuthLinearCongruentialGenerator(&rando) >> 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)) {

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 2020 Justine Alexandra Roberts Tunney
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
@ -18,22 +18,41 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/nt/thread.h"
#include "libc/nt/console.h"
#include "libc/sysv/errfuns.h"
/**
* Returns current thread id.
* @asyncsignalsafe
* Changes process group for process.
*/
uint32_t gettid(void) {
uint32_t res;
int setpgid(int pid, int pgid) {
int rc, me;
if (!IsWindows()) {
res = sys_gettid();
if (res <= 0) {
res = getpid();
}
return res;
rc = sys_setpgid(pid, pgid);
} else {
return GetCurrentThreadId();
me = getpid();
if (pid == me && pgid == me) {
/*
* "When a process is created with CREATE_NEW_PROCESS_GROUP
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
* is made on behalf of the new process; this means that the new
* process has CTRL+C disabled. This lets shells handle CTRL+C
* themselves, and selectively pass that signal on to
* sub-processes. CTRL+BREAK is not disabled, and may be used to
* interrupt the process/process group."
* Quoth MSDN § CreateProcessW()
*/
if (SetConsoleCtrlHandler(0, 1)) {
rc = 0;
} else {
rc = __winerr();
}
} else {
// irregular use cases not supported on windows
rc = einval();
}
}
STRACE("setpgid(%d, %d) → %d% m", pid, pgid, rc);
return rc;
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -24,6 +23,9 @@
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
@ -37,18 +39,6 @@
* @threadsafe
*/
#define LOCK \
for (;;) \
if (lockcmpxchg(&__sig.lock, false, true)) { \
asm volatile("" ::: "memory")
#define UNLOCK \
asm volatile("" ::: "memory"); \
__sig.lock = false; \
break; \
} \
else sched_yield()
struct Signal {
struct Signal *next;
bool used;
@ -57,7 +47,6 @@ struct Signal {
};
struct Signals {
bool lock;
sigset_t mask;
struct Signal *queue;
struct Signal mem[__SIG_QUEUE_LENGTH];
@ -67,15 +56,19 @@ struct Signals __sig; // TODO(jart): Need TLS
/**
* Allocates piece of memory for storing pending signal.
* @assume lock is held
*/
static textwindows struct Signal *__sig_alloc(void) {
int i;
struct Signal *res = 0;
for (i = 0; i < ARRAYLEN(__sig.mem); ++i) {
if (!__sig.mem[i].used && lockcmpxchg(&__sig.mem[i].used, false, true)) {
return __sig.mem + i;
if (!__sig.mem[i].used) {
__sig.mem[i].used = true;
res = __sig.mem + i;
break;
}
}
return 0;
return res;
}
/**
@ -92,7 +85,7 @@ static textwindows void __sig_free(struct Signal *mem) {
static textwindows struct Signal *__sig_remove(void) {
struct Signal *prev, *res;
if (__sig.queue) {
LOCK;
cthread_spinlock(&__sig_lock);
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
if (!sigismember(&__sig.mask, res->sig)) {
if (res == __sig.queue) {
@ -106,7 +99,7 @@ static textwindows struct Signal *__sig_remove(void) {
STRACE("%s is masked", strsignal(res->sig));
}
}
UNLOCK;
cthread_spunlock(&__sig_lock);
} else {
res = 0;
}
@ -114,28 +107,60 @@ static textwindows struct Signal *__sig_remove(void) {
}
/**
* Delivers signal.
* Delivers signal to callback.
* @note called from main thread
* @return true if EINTR should be returned by caller
*/
static textwindows bool __sig_deliver(bool restartable, struct Signal *sig,
unsigned rva) {
unsigned flags;
siginfo_t info;
flags = __sighandflags[sig->sig];
// TODO(jart): polyfill prevention of re-entry
if (flags & SA_RESETHAND) {
__sighandrvas[sig->sig] = (int32_t)(intptr_t)SIG_DFL;
static textwindows bool __sig_deliver(bool restartable, int sig, int si_code,
ucontext_t *ctx) {
unsigned rva, flags;
siginfo_t info, *infop;
STRACE("delivering %s", strsignal(sig));
// enter the signal
cthread_spinlock(&__sig_lock);
rva = __sighandrvas[sig];
flags = __sighandflags[sig];
if (~flags & SA_NODEFER) {
// by default we try to avoid reentering a signal handler. for
// example, if a sigsegv handler segfaults, then we'd want the
// second signal to just kill the process. doing this means we
// track state. that's bad if you want to longjmp() out of the
// signal handler. in that case you must use SA_NODEFER.
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
STRACE("delivering %s", strsignal(sig->sig));
bzero(&info, sizeof(info));
info.si_signo = sig->sig;
info.si_code = sig->si_code;
((sigaction_f)(_base + rva))(sig->sig, &info, 0);
cthread_spunlock(&__sig_lock);
// setup the somewhat expensive information args
// only if they're requested by the user in sigaction()
if (flags & SA_SIGINFO) {
bzero(&info, sizeof(info));
info.si_signo = sig;
info.si_code = si_code;
infop = &info;
} else {
infop = 0;
ctx = 0;
}
// handover control to user
((sigaction_f)(_base + rva))(sig, infop, ctx);
// leave the signal
cthread_spinlock(&__sig_lock);
if (~flags & SA_NODEFER) {
_cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
}
if (flags & SA_RESETHAND) {
STRACE("resetting oneshot signal handler");
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
cthread_spunlock(&__sig_lock);
if (!restartable) {
return true; // always send EINTR for wait4(), poll(), etc.
} else if (flags & SA_RESTART) {
STRACE("restarting syscall on %s", strsignal(sig->sig));
STRACE("restarting syscall on %s", strsignal(sig));
return false; // resume syscall for read(), write(), etc.
} else {
return true; // default course is to raise EINTR
@ -149,27 +174,86 @@ static textwindows bool __sig_isfatal(int sig) {
return sig != SIGCHLD;
}
/**
* Handles signal.
*
* @param restartable can be used to suppress true return if SA_RESTART
* @return true if signal was delivered
*/
textwindows bool __sig_handle(bool restartable, int sig, int si_code,
ucontext_t *ctx) {
bool delivered;
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
if (__sig_isfatal(sig)) {
STRACE("terminating on %s", strsignal(sig));
__restorewintty();
_Exit(128 + sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %s", strsignal(sig));
delivered = false;
break;
default:
delivered = __sig_deliver(restartable, sig, si_code, ctx);
break;
}
return delivered;
}
/**
* Handles signal immediately if not blocked.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @return 1 if delivered, 0 if enqueued, otherwise -1 w/ errno
* @note called from main thread
* @threadsafe
*/
textwindows int __sig_raise(int sig, int si_code) {
int rc;
int candeliver;
cthread_spinlock(&__sig_lock);
candeliver = !sigismember(&__sig.mask, sig);
cthread_spunlock(&__sig_lock);
switch (candeliver) {
case 1:
__sig_handle(false, sig, si_code, 0);
return 0;
case 0:
STRACE("%s is masked", strsignal(sig));
return __sig_add(sig, si_code);
default:
return -1; // sigismember() validates `sig`
}
}
/**
* Enqueues generic signal for delivery on New Technology.
* @return 0 if enqueued, otherwise -1 w/ errno
* @threadsafe
*/
textwindows int __sig_add(int sig, int si_code) {
int rc;
struct Signal *mem;
if (1 <= sig && sig <= NSIG) {
STRACE("enqueuing %s", strsignal(sig));
cthread_spinlock(&__sig_lock);
if ((mem = __sig_alloc())) {
mem->sig = sig;
mem->si_code = si_code;
LOCK;
mem->next = __sig.queue;
__sig.queue = mem;
UNLOCK;
return 0;
rc = 0;
} else {
return enomem();
rc = enomem();
}
cthread_spunlock(&__sig_lock);
} else {
return einval();
rc = einval();
}
return rc;
}
/**
@ -186,21 +270,7 @@ textwindows bool __sig_check(bool restartable) {
struct Signal *sig;
delivered = false;
while ((sig = __sig_remove())) {
switch ((rva = __sighandrvas[sig->sig])) {
case (intptr_t)SIG_DFL:
if (__sig_isfatal(sig->sig)) {
STRACE("terminating on %s", strsignal(sig->sig));
__restorewintty();
_Exit(128 + sig->sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %s", strsignal(sig->sig));
break;
default:
delivered = __sig_deliver(restartable, sig, rva);
break;
}
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
__sig_free(sig);
}
return delivered;
@ -208,13 +278,31 @@ textwindows bool __sig_check(bool restartable) {
/**
* Changes signal mask for main thread.
* @return old mask
* @return 0 on success, or -1 w/ errno
*/
textwindows sigset_t __sig_mask(const sigset_t *neu) {
sigset_t old;
LOCK;
old = __sig.mask;
if (neu) __sig.mask = *neu;
UNLOCK;
return old;
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
int i;
uint64_t a, b;
if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
cthread_spinlock(&__sig_lock);
if (old) {
*old = __sig.mask;
}
if (neu) {
for (i = 0; i < ARRAYLEN(__sig.mask.__bits); ++i) {
if (how == SIG_BLOCK) {
__sig.mask.__bits[i] |= neu->__bits[i];
} else if (how == SIG_UNBLOCK) {
__sig.mask.__bits[i] &= ~neu->__bits[i];
} else {
__sig.mask.__bits[i] = neu->__bits[i];
}
}
__sig.mask.__bits[0] &= ~(SIGKILL | SIGSTOP);
}
cthread_spunlock(&__sig_lock);
return 0;
} else {
return einval();
}
}

View file

@ -1,16 +1,20 @@
#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"
#define __SIG_QUEUE_LENGTH 8
#define __SIG_POLLING_INTERVAL_MS 50
#define __SIG_LOGGING_INTERVAL_MS 1700
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
bool __sig_check(bool) hidden;
bool __sig_handle(bool, int, int, ucontext_t *) hidden;
int __sig_add(int, int) hidden;
sigset_t __sig_mask(const sigset_t *) hidden;
int __sig_mask(int, const sigset_t *, sigset_t *) hidden;
int __sig_raise(int, int) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -32,6 +32,7 @@
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
@ -212,6 +213,7 @@ static int __sigaction(int sig, const struct sigaction *act,
rc = 0;
}
if (rc != -1 && !__vforked) {
cthread_spinlock(&__sig_lock);
if (oldact) {
oldrva = __sighandrvas[sig];
oldact->sa_sigaction = (sigaction_f)(
@ -221,6 +223,7 @@ static int __sigaction(int sig, const struct sigaction *act,
__sighandrvas[sig] = rva;
__sighandflags[sig] = act->sa_flags;
}
cthread_spunlock(&__sig_lock);
}
return rc;
}
@ -233,6 +236,58 @@ static int __sigaction(int sig, const struct sigaction *act,
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
*
* Here's an example of the most professional way to handle signals.
* It's generally a best practice to have signal handlers do the fewest
* number of things possible. The trick is to have your signals work
* hand-in-glove with the EINTR errno returned by i/o.
*
* static volatile bool gotctrlc;
* void OnCtrlC(int sig) {
* gotctrlc = true;
* }
* int main() {
* size_t got;
* ssize_t rc;
* char buf[1];
* struct sigaction oldint;
* struct sigaction saint = {.sa_handler = GotCtrlC};
* if (sigaction(SIGINT, &saint, &oldint) == -1) {
* perror("sigaction");
* exit(1);
* }
* for (;;) {
* rc = read(0, buf, sizeof(buf));
* if (rc == -1) {
* if (errno == EINTR) {
* if (gotctrlc) {
* break;
* }
* } else {
* perror("read");
* exit(2);
* }
* }
* if (!(got = rc)) {
* break;
* }
* for (;;) {
* rc = write(1, buf, got);
* if (rc != -1) {
* assert(rc == 1);
* break;
* } else if (errno != EINTR) {
* perror("write");
* exit(3);
* }
* }
* }
* sigaction(SIGINT, &oldint, 0);
* }
*
* Please note that you can't do the above if you use SA_RESTART. Since
* the purpose of SA_RESTART is to restart i/o operations whose docs say
* that they're @restartable and read() is one such function.
*
* @return 0 on success or -1 w/ errno
* @see xsigaction() for a much better api
* @asyncsignalsafe
@ -241,7 +296,11 @@ static int __sigaction(int sig, const struct sigaction *act,
int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
int rc;
char buf[2][128];
rc = __sigaction(sig, act, oldact);
if (sig == SIGKILL || sig == SIGSTOP) {
rc = einval();
} else {
rc = __sigaction(sig, act, oldact);
}
STRACE("sigaction(%s, %s, [%s]) → %d% m", strsignal(sig),
__strace_sigaction(buf[0], sizeof(buf[0]), 0, act),
__strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc);

View file

@ -20,104 +20,12 @@
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigaction-freebsd.internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/ucontext-freebsd.internal.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/ucontext.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
union sigval_freebsd {
int32_t sival_int;
void *sival_ptr;
int32_t sigval_int;
void *sigval_ptr;
};
struct siginfo_freebsd {
int32_t si_signo;
int32_t si_errno;
int32_t si_code;
int32_t si_pid;
uint32_t si_uid;
int32_t si_status;
void *si_addr;
union sigval_freebsd si_value;
union {
struct {
int32_t _trapno;
} _fault;
struct {
int32_t _timerid;
int32_t _overrun;
} _timer;
struct {
int32_t _mqd;
} _mesgq;
struct {
int64_t _band;
} _poll;
struct {
int64_t __spare1__;
int32_t __spare2__[7];
} __spare__;
} _reason;
};
struct stack_freebsd {
void *ss_sp;
uint64_t ss_size;
int32_t ss_flags;
};
struct mcontext_freebsd {
int64_t mc_onstack;
int64_t mc_rdi;
int64_t mc_rsi;
int64_t mc_rdx;
int64_t mc_rcx;
int64_t mc_r8;
int64_t mc_r9;
int64_t mc_rax;
int64_t mc_rbx;
int64_t mc_rbp;
int64_t mc_r10;
int64_t mc_r11;
int64_t mc_r12;
int64_t mc_r13;
int64_t mc_r14;
int64_t mc_r15;
uint32_t mc_trapno;
uint16_t mc_fs;
uint16_t mc_gs;
int64_t mc_addr;
uint32_t mc_flags;
uint16_t mc_es;
uint16_t mc_ds;
int64_t mc_err;
int64_t mc_rip;
int64_t mc_cs;
int64_t mc_rflags;
int64_t mc_rsp;
int64_t mc_ss;
int64_t mc_len;
int64_t mc_fpformat;
int64_t mc_ownedfp;
int64_t mc_fpstate[64];
int64_t mc_fsbase;
int64_t mc_gsbase;
int64_t mc_xfpustate;
int64_t mc_xfpustate_len;
int64_t mc_spare[4];
};
struct ucontext_freebsd {
struct sigset_freebsd uc_sigmask;
struct mcontext_freebsd uc_mcontext;
struct ucontext_freebsd *uc_link;
struct stack_freebsd uc_stack;
int32_t uc_flags;
int32_t __spare__[4];
};
void __sigenter_freebsd(int sig, struct siginfo_freebsd *si,
struct ucontext_freebsd *ctx) {
int rva;

View file

@ -21,12 +21,20 @@
#include "libc/sysv/consts/sa.h"
/**
* Installs kernel interrupt handler.
* Installs kernel interrupt handler, e.g.
*
* void GotCtrlC(int sig) { ... }
* CHECK_NE(SIG_ERR, signal(SIGINT, GotCtrlC));
*
* @return old signal handler on success or SIG_ERR w/ errno
* @note this function has BSD semantics, i.e. SA_RESTART
* @see sigaction() which has more features
*/
sighandler_t(signal)(int sig, sighandler_t func) {
struct sigaction sa_old, sa = {.sa_handler = func, .sa_flags = SA_RESTART};
if ((sigaction)(sig, &sa, &sa_old) == -1) return SIG_ERR;
return sa_old.sa_handler;
struct sigaction old, sa = {.sa_handler = func, .sa_flags = SA_RESTART};
if ((sigaction)(sig, &sa, &old) != -1) {
return old.sa_handler;
} else {
return SIG_ERR;
}
}

View file

@ -62,6 +62,8 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
char buf[2][41];
int res, rc, arg1;
const sigset_t *arg2;
STRACE("sigprocmask(%s, %s, [...]", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set));
sigemptyset(&old);
if (IsAsan() &&
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
@ -86,14 +88,13 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
rc = -1;
}
} else { // windows or metal
old = __sig_mask(opt_set);
rc = __sig_mask(how, opt_set, &old);
_check_interrupts(false, 0);
rc = 0;
}
if (rc != -1 && opt_out_oldset) {
*opt_out_oldset = old;
}
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
STRACE("[...] sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),
__strace_sigset(buf[1], sizeof(buf[1]), rc, opt_out_oldset), rc);
return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -24,20 +25,23 @@
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/log/backtrace.internal.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
/**
* Blocks until SIG MASK is delivered to process.
*
* @param ignore is a bitset of signals to block temporarily
* @return -1 w/ EINTR or possibly EFAULT
* @param ignore is a bitset of signals to block temporarily, which if
* NULL is equivalent to passing an empty signal set
* @return -1 w/ EINTR (or possibly EFAULT)
* @asyncsignalsafe
* @norestart
*/
int sigsuspend(const sigset_t *ignore) {
int rc;
char buf[41];
long ms, totoms;
sigset_t save, mask, *arg;
STRACE("sigsuspend(%s) → [...]",
__strace_sigset(buf, sizeof(buf), 0, ignore));
@ -62,15 +66,24 @@ int sigsuspend(const sigset_t *ignore) {
if (!IsWindows()) {
rc = sys_sigsuspend(arg, 8);
} else {
save = __sig_mask(arg);
__sig_mask(SIG_SETMASK, arg, &save);
ms = 0;
totoms = 0;
do {
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
break;
}
SleepEx(__SIG_POLLING_INTERVAL_MS, true);
#ifdef SYSDEBUG
ms += __SIG_POLLING_INTERVAL_MS;
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
totoms += ms, ms = 0;
STRACE("[...] sigsuspending for %'lums...", totoms);
}
#endif
} while (1);
__sig_mask(&save);
__sig_mask(SIG_SETMASK, &save, 0);
}
} else {
// TODO(jart): sigsuspend metal support

View file

@ -3,6 +3,7 @@
#include "libc/calls/struct/sigset.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/typedef/sighandler_t.h"
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
struct sigaction { /* cosmo abi */

View file

@ -0,0 +1,102 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_
#include "libc/calls/struct/sigaction-freebsd.internal.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
union sigval_freebsd {
int32_t sival_int;
void *sival_ptr;
int32_t sigval_int;
void *sigval_ptr;
};
struct siginfo_freebsd {
int32_t si_signo;
int32_t si_errno;
int32_t si_code;
int32_t si_pid;
uint32_t si_uid;
int32_t si_status;
void *si_addr;
union sigval_freebsd si_value;
union {
struct {
int32_t _trapno;
} _fault;
struct {
int32_t _timerid;
int32_t _overrun;
} _timer;
struct {
int32_t _mqd;
} _mesgq;
struct {
int64_t _band;
} _poll;
struct {
int64_t __spare1__;
int32_t __spare2__[7];
} __spare__;
} _reason;
};
struct stack_freebsd {
void *ss_sp;
uint64_t ss_size;
int32_t ss_flags;
};
struct mcontext_freebsd {
int64_t mc_onstack;
int64_t mc_rdi;
int64_t mc_rsi;
int64_t mc_rdx;
int64_t mc_rcx;
int64_t mc_r8;
int64_t mc_r9;
int64_t mc_rax;
int64_t mc_rbx;
int64_t mc_rbp;
int64_t mc_r10;
int64_t mc_r11;
int64_t mc_r12;
int64_t mc_r13;
int64_t mc_r14;
int64_t mc_r15;
uint32_t mc_trapno;
uint16_t mc_fs;
uint16_t mc_gs;
int64_t mc_addr;
uint32_t mc_flags;
uint16_t mc_es;
uint16_t mc_ds;
int64_t mc_err;
int64_t mc_rip;
int64_t mc_cs;
int64_t mc_rflags;
int64_t mc_rsp;
int64_t mc_ss;
int64_t mc_len;
int64_t mc_fpformat;
int64_t mc_ownedfp;
int64_t mc_fpstate[64];
int64_t mc_fsbase;
int64_t mc_gsbase;
int64_t mc_xfpustate;
int64_t mc_xfpustate_len;
int64_t mc_spare[4];
};
struct ucontext_freebsd {
struct sigset_freebsd uc_sigmask;
struct mcontext_freebsd uc_mcontext;
struct ucontext_freebsd *uc_link;
struct stack_freebsd uc_stack;
int32_t uc_flags;
int32_t __spare__[4];
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_FREEBSD_INTERNAL_H_ */

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/intrin/lockxchg.h"
/**
* Deletes file.

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/rusage.h"
@ -40,11 +41,13 @@
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {
static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
int options,
struct rusage *opt_out_rusage) {
int pids[64];
int64_t handle;
int64_t handles[64];
@ -129,3 +132,15 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
return pids[i];
}
}
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {
int rc;
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
rc = sys_wait4_nt_impl(pid, opt_out_wstatus, options, opt_out_rusage);
__sig_mask(SIG_SETMASK, &oldmask, 0);
return rc;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/ucontext.h"
@ -24,14 +25,14 @@
#include "libc/nt/enum/signal.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
int sig, rva, code;
struct Goodies {
ucontext_t ctx;
struct siginfo si;
} g;
int64_t rip;
int sig, code;
ucontext_t ctx;
STRACE("__wincrash");
switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint:
@ -96,12 +97,22 @@ textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
default:
return kNtExceptionContinueSearch;
}
bzero(&g, sizeof(g));
g.si.si_code = code;
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
ntcontext2linux(&g.ctx, ep->ContextRecord);
((sigaction_f)(_base + rva))(sig, &g.si, &g.ctx);
rip = ep->ContextRecord->Rip;
if (__sighandflags[sig] & SA_SIGINFO) {
_ntcontext2linux(&ctx, ep->ContextRecord);
__sig_handle(false, sig, code, &ctx);
_ntlinux2context(ep->ContextRecord, &ctx);
} else {
__sig_handle(false, sig, code, 0);
}
// Windows seems to be the only operating system that traps INT3 at
// addressof(INT3) rather than addressof(INT3)+1. So we must adjust
// RIP to prevent the same INT3 from being trapped forevermore.
if (sig == SIGTRAP && rip == ep->ContextRecord->Rip) {
ep->ContextRecord->Rip++;
}
return kNtExceptionContinueExecution;
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/siginfo.h"
@ -29,22 +30,10 @@
#include "libc/nt/struct/overlapped.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
static textwindows ssize_t sys_write_nt_epipe(int fd) {
siginfo_t info;
STRACE("WriteFile(%d:%p) → %m", fd, g_fds.p[fd].handle);
if (!__sighandrvas[SIGPIPE]) {
__restorewintty();
_Exit(128 + SIGPIPE);
} else if (__sighandrvas[SIGPIPE] >= kSigactionMinRva) {
bzero(&info, sizeof(info));
((sigaction_f)(_base + __sighandrvas[SIGPIPE]))(SIGPIPE, &info, 0);
}
return epipe();
}
static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
ssize_t offset) {
uint32_t sent;
@ -53,7 +42,8 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
_offset2overlap(offset, &overlap))) {
return sent;
} else if (GetLastError() == kNtErrorBrokenPipe) {
return sys_write_nt_epipe(fd);
__sig_raise(SIGPIPE, SI_KERNEL);
return epipe();
} else {
return __winerr();
}