mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 20:13:31 +00:00
498 lines
18 KiB
C
498 lines
18 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 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/sigset.internal.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/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/runtime/runtime.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 sigaction_f __sig_handler(unsigned rva) {
|
|
return (sigaction_f)(__executable_start + rva);
|
|
}
|
|
|
|
static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx,
|
|
unsigned rva, unsigned flags,
|
|
struct CosmoTib *tib) {
|
|
++__sig.count;
|
|
if (__sig_should_use_altstack(flags, tib)) {
|
|
tib->tib_sigstack_flags |= SS_ONSTACK;
|
|
__stack_call(sig, si, ctx, 0, __sig_handler(rva),
|
|
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
|
tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
|
} else {
|
|
__sig_handler(rva)(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);
|
|
if (!(flags & SA_NODEFER)) {
|
|
tib->tib_sigmask |= 1ull << (sig - 1);
|
|
}
|
|
STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
|
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
|
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}));
|
|
__sig_call(sig, &si, &ctx, rva, flags, tib);
|
|
STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
|
__sig_handler(rva),
|
|
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}),
|
|
DescribeSigset(0, &ctx.uc_sigmask));
|
|
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))) {
|
|
STRACE("canceling futex");
|
|
_weaken(WakeByAddressSingle)(futex);
|
|
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
|
|
STRACE("canceling i/o operation");
|
|
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) {
|
|
STRACE("canceling semaphore");
|
|
pt->pt_flags &= ~PT_INSEMAPHORE;
|
|
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
|
|
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
|
|
}
|
|
} else {
|
|
STRACE("canceling asynchronously");
|
|
}
|
|
}
|
|
|
|
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};
|
|
int sig = sf->si.si_signo;
|
|
_ntcontext2linux(&ctx, sf->nc);
|
|
ctx.uc_sigmask.__bits[0] =
|
|
atomic_load_explicit(&sf->pt->tib->tib_sigmask, memory_order_acquire);
|
|
if (!(sf->flags & SA_NODEFER)) {
|
|
sf->pt->tib->tib_sigmask |= 1ull << (sig - 1);
|
|
}
|
|
++__sig.count;
|
|
STRACE("entering __sig_tramp(%G, %t) with mask %s → %s", sig,
|
|
__sig_handler(sf->rva), DescribeSigset(0, &ctx.uc_sigmask),
|
|
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}));
|
|
__sig_handler(sf->rva)(sig, &sf->si, &ctx);
|
|
STRACE("leaving __sig_tramp(%G, %t) with mask %s → %s", sig,
|
|
__sig_handler(sf->rva),
|
|
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}),
|
|
DescribeSigset(0, &ctx.uc_sigmask));
|
|
atomic_store_explicit(&sf->pt->tib->tib_sigmask, ctx.uc_sigmask.__bits[0],
|
|
memory_order_release);
|
|
// TDOO(jart): Do we need to do a __sig_check() here?
|
|
_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
|
|
pt->abort_errno = EINTR;
|
|
__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) {
|
|
int handler_was_called = 0;
|
|
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) {
|
|
int 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() → %d", 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__ */
|