Trim down MODE= linkage slightly

This commit is contained in:
Justine Tunney 2022-04-12 08:05:22 -07:00
parent 046c7ebd4a
commit c95c9d9508
13 changed files with 315 additions and 283 deletions

View file

@ -46,6 +46,6 @@ int kill(int pid, int sig) {
} else {
rc = sys_kill_nt(pid, sig);
}
STRACE("kill(%d, %s) → %d% m", pid, strsignal(sig), rc);
STRACE("kill(%d, %G) → %d% m", pid, sig, rc);
return rc;
}

View file

@ -45,7 +45,7 @@ static textwindows inline bool HasWorkingConsole(void) {
*/
int raise(int sig) {
int rc, event;
STRACE("raise(%s) → [...]", strsignal(sig));
STRACE("raise(%G) → [...]", sig);
if (sig == SIGTRAP) {
DebugBreak();
rc = 0;
@ -75,6 +75,6 @@ int raise(int sig) {
rc = __sig_raise(sig, SI_USER);
}
}
STRACE("[...] raise(%s) → %d% m", strsignal(sig), rc);
STRACE("[...] raise(%G) → %d% m", sig, rc);
return rc;
}

View file

@ -16,266 +16,20 @@
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/sig.internal.h"
#include "libc/calls/sigbits.h"
#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"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
/**
* @fileoverview UNIX signals for the New Technology.
* @fileoverview UNIX signals for the New Technology, Part 1.
* @threadsafe
*/
struct Signal {
struct Signal *next;
bool used;
int sig;
int si_code;
};
struct Signals {
sigset_t mask;
struct Signal *queue;
struct Signal mem[__SIG_QUEUE_LENGTH];
};
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) {
__sig.mem[i].used = true;
res = __sig.mem + i;
break;
}
}
return res;
}
/**
* Returns signal memory to static pool.
*/
static textwindows void __sig_free(struct Signal *mem) {
mem->used = false;
}
/**
* Dequeues signal that isn't masked.
* @return signal or null if empty or none unmasked
*/
static textwindows struct Signal *__sig_remove(void) {
struct Signal *prev, *res;
if (__sig.queue) {
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) {
__sig.queue = res->next;
} else if (prev) {
prev->next = res->next;
}
res->next = 0;
break;
} else {
STRACE("%s is masked", strsignal(res->sig));
}
}
cthread_spunlock(&__sig_lock);
} else {
res = 0;
}
return res;
}
/**
* 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, 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;
}
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));
return false; // resume syscall for read(), write(), etc.
} else {
return true; // default course is to raise EINTR
}
}
/**
* Returns true if signal default action is to end process.
*/
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;
mem->next = __sig.queue;
__sig.queue = mem;
rc = 0;
} else {
rc = enomem();
}
cthread_spunlock(&__sig_lock);
} else {
rc = einval();
}
return rc;
}
/**
* Enqueues generic signal for delivery on New Technology.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @note called from main thread
* @threadsafe
*/
textwindows bool __sig_check(bool restartable) {
unsigned rva;
bool delivered;
struct Signal *sig;
delivered = false;
while ((sig = __sig_remove())) {
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
__sig_free(sig);
}
return delivered;
}
/**
* Changes signal mask for main thread.
* @return 0 on success, or -1 w/ errno

View file

@ -10,6 +10,21 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Signal {
struct Signal *next;
bool used;
int sig;
int si_code;
};
struct Signals {
sigset_t mask;
struct Signal *queue;
struct Signal mem[__SIG_QUEUE_LENGTH];
};
extern struct Signals __sig; // TODO(jart): Need TLS
bool __sig_check(bool) hidden;
bool __sig_handle(bool, int, int, ucontext_t *) hidden;
int __sig_add(int, int) hidden;

256
libc/calls/sig2.c Normal file
View file

@ -0,0 +1,256 @@
/*-*- 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/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/errfuns.h"
/**
* @fileoverview UNIX signals for the New Technology, Part 2.
* @threadsafe
*/
/**
* 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) {
__sig.mem[i].used = true;
res = __sig.mem + i;
break;
}
}
return res;
}
/**
* Returns signal memory to static pool.
*/
static textwindows void __sig_free(struct Signal *mem) {
mem->used = false;
}
/**
* Dequeues signal that isn't masked.
* @return signal or null if empty or none unmasked
*/
static textwindows struct Signal *__sig_remove(void) {
struct Signal *prev, *res;
if (__sig.queue) {
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) {
__sig.queue = res->next;
} else if (prev) {
prev->next = res->next;
}
res->next = 0;
break;
} else {
STRACE("%G is masked", res->sig);
}
}
cthread_spunlock(&__sig_lock);
} else {
res = 0;
}
return res;
}
/**
* 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, int sig, int si_code,
ucontext_t *ctx) {
unsigned rva, flags;
siginfo_t info, *infop;
STRACE("delivering %G", 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;
}
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 %G", sig);
return false; // resume syscall for read(), write(), etc.
} else {
return true; // default course is to raise EINTR
}
}
/**
* Returns true if signal default action is to end process.
*/
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 %G", sig);
__restorewintty();
_Exit(128 + sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %G", 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("%G is masked", 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 %G", sig);
cthread_spinlock(&__sig_lock);
if ((mem = __sig_alloc())) {
mem->sig = sig;
mem->si_code = si_code;
mem->next = __sig.queue;
__sig.queue = mem;
rc = 0;
} else {
rc = enomem();
}
cthread_spunlock(&__sig_lock);
} else {
rc = einval();
}
return rc;
}
/**
* Enqueues generic signal for delivery on New Technology.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @note called from main thread
* @threadsafe
*/
textwindows bool __sig_check(bool restartable) {
unsigned rva;
bool delivered;
struct Signal *sig;
delivered = false;
while ((sig = __sig_remove())) {
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
__sig_free(sig);
}
return delivered;
}

View file

@ -301,7 +301,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
} else {
rc = __sigaction(sig, act, oldact);
}
STRACE("sigaction(%s, %s, [%s]) → %d% m", strsignal(sig),
STRACE("sigaction(%G, %s, [%s]) → %d% m", sig,
__strace_sigaction(buf[0], sizeof(buf[0]), 0, act),
__strace_sigaction(buf[1], sizeof(buf[1]), rc, oldact), rc);
return rc;

View file

@ -498,6 +498,21 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
goto FormatDecimal;
}
case 'G':
x = va_arg(va, int);
if (weaken(strsignal)) {
s = weaken(strsignal)(x);
goto FormatString;
} else {
if (p + 3 <= e) {
p[0] = 'S';
p[1] = 'I';
p[2] = 'G';
}
p += 3;
goto FormatDecimal;
}
case 'n':
// nonstandard %n specifier
// used to print newlines that work in raw terminal modes
@ -823,6 +838,7 @@ privileged void kvprintf(const char *fmt, va_list v) {
* - `u` unsigned
* - `r` carriage
* - `m` strerror
* - `G` strsignal
* - `X` uppercase
* - `T` timestamp
* - `x` hexadecimal

View file

@ -41,6 +41,7 @@
*/
STATIC_YOINK("strerror_r"); /* for kprintf %m */
STATIC_YOINK("strsignal"); /* for kprintf %G */
static const char kGregOrder[17] forcealign(1) = {
13, 11, 8, 14, 12, 9, 10, 15, 16, 0, 1, 2, 3, 4, 5, 6, 7,
@ -54,29 +55,8 @@ static const char kGregNames[17][4] forcealign(1) = {
static const char kCpuFlags[12] forcealign(1) = "CVPRAKZSTIDO";
static const char kFpuExceptions[6] forcealign(1) = "IDZOUP";
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
int kCrashSigs[7];
struct sigaction g_oldcrashacts[7];
static const char kCrashSigNames[7][5] forcealign(1) = {
"QUIT", //
"FPE", //
"ILL", //
"SEGV", //
"TRAP", //
"ABRT", //
"BUS", //
};
/* </SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
static relegated noinstrument const char *TinyStrSignal(int sig) {
size_t i;
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
if (kCrashSigs[i] && sig == kCrashSigs[i]) {
return kCrashSigNames[i];
}
}
return "???";
}
relegated static void ShowFunctionCalls(ucontext_t *ctx) {
struct StackFrame *bp;
@ -222,12 +202,11 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
uname(&names);
p = buf;
errno = err;
kprintf("%n%serror%s: Uncaught SIG%s (%s) on %s pid %d%n"
kprintf("%n%serror%s: Uncaught %G (%s) on %s pid %d%n"
" %s%n"
" %m%n"
" %s %s %s %s%n",
!__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "",
TinyStrSignal(sig),
!__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "", sig,
(ctx && (ctx->uc_mcontext.rsp >= GetStaticStackAddr(0) &&
ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + PAGESIZE))
? "Stack Overflow"
@ -269,13 +248,13 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
const char *kind) {
kprintf("%n"
"%n"
"CRASHED %s WITH SIG%s%n"
"CRASHED %s WITH %G%n"
"%s%n"
"RIP %x%n"
"RSP %x%n"
"RBP %x%n"
"%n",
kind, TinyStrSignal(sig), __argv[0], ctx ? ctx->uc_mcontext.rip : 0,
kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0,
ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0);
__restorewintty();
_Exit(119);

View file

@ -1,2 +1,15 @@
.include "o/libc/nt/codegen.inc"
.imp kernel32,__imp_SetDefaultDllDirectories,SetDefaultDllDirectories,0
.text.windows
SetDefaultDllDirectories:
push %rbp
mov %rsp,%rbp
.profilable
mov %rdi,%rcx
sub $32,%rsp
call *__imp_SetDefaultDllDirectories(%rip)
leave
ret
.endfn SetDefaultDllDirectories,globl
.previous

View file

@ -1,5 +1,5 @@
.include "o/libc/nt/codegen.inc"
.imp kernel32,__imp_SetLastError,SetLastError,1336
.imp kernel32,__imp_SetLastError,SetLastError,0
.text.windows
SetLastError:

View file

@ -1103,7 +1103,7 @@ imp 'SetCurrentDirectory' SetCurrentDirectoryW kernel32 0 1
imp 'SetCurrentDirectoryA' SetCurrentDirectoryA kernel32 0 1
imp 'SetDefaultCommConfig' SetDefaultCommConfigW kernel32 1298
imp 'SetDefaultCommConfigA' SetDefaultCommConfigA kernel32 1297
imp 'SetDefaultDllDirectories' SetDefaultDllDirectories kernel32 0 1, Windows 8+, KB2533623 on Windows 7
imp 'SetDefaultDllDirectories' SetDefaultDllDirectories kernel32 0 1 # Windows 8+, KB2533623 on Windows 7
imp 'SetDllDirectory' SetDllDirectoryW kernel32 1301
imp 'SetDllDirectoryA' SetDllDirectoryA kernel32 1300
imp 'SetDynamicTimeZoneInformation' SetDynamicTimeZoneInformation kernel32 0
@ -1137,7 +1137,7 @@ imp 'SetHandleCount' SetHandleCount kernel32 0 1
imp 'SetHandleInformation' SetHandleInformation kernel32 0 3
imp 'SetInformationJobObject' SetInformationJobObject kernel32 1333
imp 'SetIoRateControlInformationJobObject' SetIoRateControlInformationJobObject kernel32 1334
imp 'SetLastError' SetLastError kernel32 1336 1
imp 'SetLastError' SetLastError kernel32 0 1
imp 'SetLocalPrimaryComputerName' SetLocalPrimaryComputerNameW kernel32 1338
imp 'SetLocalPrimaryComputerNameA' SetLocalPrimaryComputerNameA kernel32 1337
imp 'SetLocalTime' SetLocalTime kernel32 0

View file

@ -109,7 +109,6 @@ noasan int munmap(void *v, size_t n) {
} else {
rc = -1;
}
STRACE("munmap(%.12p, %'zu) → %d %s", p, n, rc,
rc == -1 ? strerror(errno) : "");
STRACE("munmap(%.12p, %'zu) → %d% m", p, n, rc);
return rc;
}

View file

@ -151,7 +151,7 @@ textstartup void __printargs(void) {
PRINT("BLOCKED SIGNALS {%#lx, %#lx}", ss.__bits[0], ss.__bits[1]);
for (i = 0; i < 32; ++i) {
if (ss.__bits[0] & (1u << i)) {
PRINT(" ☼ %s (%d)", strsignal(i + 1), i + 1);
PRINT(" ☼ %G (%d)", i + 1, i + 1);
}
}
}