diff --git a/Makefile b/Makefile index b9809ad06..984470e26 100644 --- a/Makefile +++ b/Makefile @@ -107,6 +107,7 @@ include libc/fmt/fmt.mk #─┘ include libc/calls/calls.mk #─┐ include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME include libc/crt/crt.mk # │ You can issue system calls +include libc/thread/thread.mk # │ include libc/rand/rand.mk # │ include libc/unicode/unicode.mk # │ include third_party/dlmalloc/dlmalloc.mk #─┘ @@ -117,7 +118,6 @@ include libc/time/time.mk # │ You can finally call malloc() include libc/alg/alg.mk # │ include libc/stdio/stdio.mk # │ include third_party/libcxx/libcxx.mk # │ -include libc/thread/thread.mk # │ include net/net.mk # │ include libc/log/log.mk # │ include third_party/bzip2/bzip2.mk # │ @@ -282,7 +282,6 @@ COSMOPOLITAN_OBJECTS = \ LIBC_NT_PSAPI \ LIBC_NT_POWERPROF \ LIBC_NT_PDH \ - LIBC_NT_KERNELBASE \ LIBC_NT_SHELL32 \ LIBC_NT_GDI32 \ LIBC_NT_COMDLG32 \ diff --git a/examples/forkrand.c b/examples/forkrand.c index 0eaec9e8c..7f81afb6b 100644 --- a/examples/forkrand.c +++ b/examples/forkrand.c @@ -8,6 +8,7 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/nt/nt/process.h" diff --git a/libc/bits/asmflag.h b/libc/bits/asmflag.h new file mode 100644 index 000000000..64013b14b --- /dev/null +++ b/libc/bits/asmflag.h @@ -0,0 +1,34 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_ +#define COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +/* + * Constraints for virtual machine flags. + * @note we beseech clang devs for flag constraints + */ +#ifdef __GCC_ASM_FLAG_OUTPUTS__ /* GCC6+ CLANG10+ */ +#define CFLAG_CONSTRAINT "=@ccc" +#define CFLAG_ASM(OP) OP +#define ZFLAG_CONSTRAINT "=@ccz" +#define ZFLAG_ASM(OP) OP +#define OFLAG_CONSTRAINT "=@cco" +#define OFLAG_ASM(OP) OP +#define SFLAG_CONSTRAINT "=@ccs" +#define SFLAG_ASM(SP) SP +#define ABOVE_CONSTRAINT "=@cca" /* i.e. !ZF && !CF */ +#define ABOVEFLAG_ASM(OP) OP +#else +#define CFLAG_CONSTRAINT "=q" +#define CFLAG_ASM(OP) OP "\n\tsetc\t%b0" +#define ZFLAG_CONSTRAINT "=q" +#define ZFLAG_ASM(OP) OP "\n\tsetz\t%b0" +#define OFLAG_CONSTRAINT "=q" +#define OFLAG_ASM(OP) OP "\n\tseto\t%b0" +#define SFLAG_CONSTRAINT "=q" +#define SFLAG_ASM(SP) OP "\n\tsets\t%b0" +#define ABOVE_CONSTRAINT "=@cca" +#define ABOVEFLAG_ASM(OP) OP "\n\tseta\t%b0" +#endif + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_BITS_ASMFLAG_H_ */ diff --git a/libc/bits/atomic.h b/libc/bits/atomic.h index 9cb5d5571..e2d628adf 100644 --- a/libc/bits/atomic.h +++ b/libc/bits/atomic.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ #define COSMOPOLITAN_LIBC_BITS_ATOMIC_H_ #include "libc/bits/bits.h" +#include "libc/intrin/lockcmpxchg.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -31,8 +32,8 @@ COSMOPOLITAN_C_START_ }) #define atomic_init(PTR, VAL) atomic_store(PTR, VAL) #define atomic_exchange(PTR, VAL) lockxchg(PTR, &(VAL)) -#define atomic_compare_exchange_strong(X, Y, Z) lockcmpxchg(X, Y, Z) -#define atomic_compare_exchange_weak(X, Y, Z) lockcmpxchg(X, Y, Z) +#define atomic_compare_exchange_strong(X, Y, Z) _lockcmpxchg(X, Y, Z) +#define atomic_compare_exchange_weak(X, Y, Z) _lockcmpxchg(X, Y, Z) #define atomic_load_explicit(PTR, ORDER) atomic_load(PTR) #define atomic_store_explicit(PTR, VAL, ORDER) atomic_store(PTR, VAL) #define atomic_flag_clear_explicit(PTR, ORDER) atomic_store(PTR, 0) diff --git a/libc/bits/bitop.h b/libc/bits/bitop.h new file mode 100644 index 000000000..934ca0160 --- /dev/null +++ b/libc/bits/bitop.h @@ -0,0 +1,45 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_BITOP_H_ +#define COSMOPOLITAN_LIBC_BITS_BITOP_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define bts(MEM, BIT) __BitOp("bts", BIT, MEM) /** bit test and set */ +#define btr(MEM, BIT) __BitOp("btr", BIT, MEM) /** bit test and reset */ +#define btc(MEM, BIT) __BitOp("btc", BIT, MEM) /** bit test and complement */ +#define lockbts(MEM, BIT) __BitOp("lock bts", BIT, MEM) +#define lockbtr(MEM, BIT) __BitOp("lock btr", BIT, MEM) +#define lockbtc(MEM, BIT) __BitOp("lock btc", BIT, MEM) + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __BitOp(OP, BIT, MEM) \ + ({ \ + bool OldBit; \ + if (__builtin_constant_p(BIT)) { \ + asm(CFLAG_ASM(OP "%z1\t%2,%1") \ + : CFLAG_CONSTRAINT(OldBit), \ + "+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \ + : "J"((BIT) % (sizeof((MEM)[0]) * CHAR_BIT)) \ + : "cc"); \ + } else if (sizeof((MEM)[0]) == 2) { \ + asm(CFLAG_ASM(OP "\t%w2,%1") \ + : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ + : "r"(BIT) \ + : "cc"); \ + } else if (sizeof((MEM)[0]) == 4) { \ + asm(CFLAG_ASM(OP "\t%k2,%1") \ + : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ + : "r"(BIT) \ + : "cc"); \ + } else if (sizeof((MEM)[0]) == 8) { \ + asm(CFLAG_ASM(OP "\t%q2,%1") \ + : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ + : "r"(BIT) \ + : "cc"); \ + } \ + OldBit; \ + }) +#endif /* __GNUC__ && !__STRICT_ANSI__ */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_BITS_BITOP_H_ */ diff --git a/libc/bits/bits.h b/libc/bits/bits.h index 641e0c1f3..d283f9aa8 100644 --- a/libc/bits/bits.h +++ b/libc/bits/bits.h @@ -21,11 +21,6 @@ unsigned long roundup2pow(unsigned long) libcesque pureconst; unsigned long roundup2log(unsigned long) libcesque pureconst; unsigned long rounddown2pow(unsigned long) libcesque pureconst; unsigned long hamming(unsigned long, unsigned long) pureconst; -intptr_t lockxchg(void *, void *, size_t); -bool cmpxchg(void *, intptr_t, intptr_t, size_t); -bool lockcmpxchg(void *, intptr_t, intptr_t, size_t); -intptr_t atomic_load(void *, size_t); -intptr_t atomic_store(void *, intptr_t, size_t); unsigned bextra(const unsigned *, size_t, char); /*───────────────────────────────────────────────────────────────────────────│─╗ @@ -136,84 +131,6 @@ unsigned bextra(const unsigned *, size_t, char); ╚────────────────────────────────────────────────────────────────────────────│*/ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -/* - * Constraints for virtual machine flags. - * @note we beseech clang devs for flag constraints - */ -#ifdef __GCC_ASM_FLAG_OUTPUTS__ /* GCC6+ CLANG10+ */ -#define CFLAG_CONSTRAINT "=@ccc" -#define CFLAG_ASM(OP) OP -#define ZFLAG_CONSTRAINT "=@ccz" -#define ZFLAG_ASM(OP) OP -#define OFLAG_CONSTRAINT "=@cco" -#define OFLAG_ASM(OP) OP -#define SFLAG_CONSTRAINT "=@ccs" -#define SFLAG_ASM(SP) SP -#define ABOVE_CONSTRAINT "=@cca" /* i.e. !ZF && !CF */ -#define ABOVEFLAG_ASM(OP) OP -#else -#define CFLAG_CONSTRAINT "=q" -#define CFLAG_ASM(OP) OP "\n\tsetc\t%b0" -#define ZFLAG_CONSTRAINT "=q" -#define ZFLAG_ASM(OP) OP "\n\tsetz\t%b0" -#define OFLAG_CONSTRAINT "=q" -#define OFLAG_ASM(OP) OP "\n\tseto\t%b0" -#define SFLAG_CONSTRAINT "=q" -#define SFLAG_ASM(SP) OP "\n\tsets\t%b0" -#define ABOVE_CONSTRAINT "=@cca" -#define ABOVEFLAG_ASM(OP) OP "\n\tseta\t%b0" -#endif - -/** - * Reads scalar from memory w/ one operation. - * - * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @return *(MEM) - * @note defeats compiler load tearing optimizations - * @note alignas(𝑘) is implied if compiler knows type - * @note alignas(𝑘) only avoids multi-core / cross-page edge cases - * @see Intel's Six-Thousand Page Manual V.3A §8.2.3.1 - * @see atomic_store() - */ -#define atomic_load(MEM) \ - ({ \ - autotype(MEM) Mem = (MEM); \ - typeof(*Mem) Reg; \ - asm("mov\t%1,%0" : "=r"(Reg) : "m"(*Mem)); \ - Reg; \ - }) - -/** - * Saves scalar to memory w/ one operation. - * - * This is guaranteed to happen in either one or zero operations, - * depending on whether or not it's possible for *(MEM) to be read - * afterwards. This macro only forbids compiler from using >1 ops. - * - * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @param VAL is uint𝑘_t w/ better encoding for immediates (constexpr) - * @return VAL - * @note alignas(𝑘) on nexgen32e only needed for end of page gotcha - * @note alignas(𝑘) is implied if compiler knows type - * @note needed to defeat store tearing optimizations - * @see Intel Six-Thousand Page Manual Manual V.3A §8.2.3.1 - * @see atomic_load() - */ -#define atomic_store(MEM, VAL) \ - ({ \ - autotype(VAL) Val = (VAL); \ - typeof(&Val) Mem = (MEM); \ - asm("mov%z1\t%1,%0" : "=m"(*Mem) : "r"(Val)); \ - Val; \ - }) - -#define bts(MEM, BIT) __BitOp("bts", BIT, MEM) /** bit test and set */ -#define btr(MEM, BIT) __BitOp("btr", BIT, MEM) /** bit test and reset */ -#define btc(MEM, BIT) __BitOp("btc", BIT, MEM) /** bit test and complement */ -#define lockbts(MEM, BIT) __BitOp("lock bts", BIT, MEM) -#define lockbtr(MEM, BIT) __BitOp("lock btr", BIT, MEM) -#define lockbtc(MEM, BIT) __BitOp("lock btc", BIT, MEM) - #define lockinc(MEM) __ArithmeticOp1("lock inc", MEM) #define lockdec(MEM) __ArithmeticOp1("lock dec", MEM) #define locknot(MEM) __ArithmeticOp1("lock not", MEM) @@ -225,66 +142,6 @@ unsigned bextra(const unsigned *, size_t, char); #define lockandeq(MEM, VAL) __ArithmeticOp2("lock and", VAL, MEM) #define lockoreq(MEM, VAL) __ArithmeticOp2("lock or", VAL, MEM) -/** - * Exchanges *MEMORY into *LOCALVAR w/ one operation. - * - * @param MEMORY is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @param LOCALVAR is uint𝑘_t[hasatleast 1] - * @return LOCALVAR[0] - * @see xchg() - */ -#define lockxchg(MEMORY, LOCALVAR) \ - ({ \ - asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \ - *(LOCALVAR); \ - }) - -/** - * Compares and exchanges. - * - * @param IFTHING is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @return true if value was exchanged, otherwise false - * @see lockcmpxchg() - */ -#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ - ({ \ - bool DidIt; \ - autotype(IFTHING) IfThing = (IFTHING); \ - typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ - typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ - asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \ - : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ - : "r"(ReplaceItWithMe) \ - : "cc"); \ - DidIt; \ - }) - -/** - * Compares and exchanges w/ one operation. - * - * @param IFTHING is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} - * @return true if value was exchanged, otherwise false - * @see lockcmpxchg() - */ -#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ - ({ \ - bool DidIt; \ - autotype(IFTHING) IfThing = (IFTHING); \ - typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ - typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ - asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \ - : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ - : "r"(ReplaceItWithMe) \ - : "cc"); \ - DidIt; \ - }) - -#define IsAddressCanonicalForm(P) \ - ({ \ - intptr_t p2 = (intptr_t)(P); \ - (0xffff800000000000l <= p2 && p2 <= 0x00007fffffffffffl); \ - }) - /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § bits » implementation details ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ @@ -301,44 +158,6 @@ unsigned bextra(const unsigned *, size_t, char); MEM; \ }) -#define __BitOp(OP, BIT, MEM) \ - ({ \ - bool OldBit; \ - if (__builtin_constant_p(BIT)) { \ - asm(CFLAG_ASM(OP "%z1\t%2,%1") \ - : CFLAG_CONSTRAINT(OldBit), \ - "+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \ - : "J"((BIT) % (sizeof((MEM)[0]) * CHAR_BIT)) \ - : "cc"); \ - } else if (sizeof((MEM)[0]) == 2) { \ - asm(CFLAG_ASM(OP "\t%w2,%1") \ - : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ - : "r"(BIT) \ - : "cc"); \ - } else if (sizeof((MEM)[0]) == 4) { \ - asm(CFLAG_ASM(OP "\t%k2,%1") \ - : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ - : "r"(BIT) \ - : "cc"); \ - } else if (sizeof((MEM)[0]) == 8) { \ - asm(CFLAG_ASM(OP "\t%q2,%1") \ - : CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \ - : "r"(BIT) \ - : "cc"); \ - } \ - OldBit; \ - }) - -#else -#define cmpxchg(MEM, CMP, VAL) \ - cmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) -#define lockcmpxchg(MEM, CMP, VAL) \ - lockcmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) -#define lockxchg(MEM, VAR) \ - lockxchg(MEM, VAR, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAR)))) -#define atomic_store(MEM, VAL) \ - atomic_store(MEM, VAL, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAL)))) -#define atomic_load(MEM) atomic_load(MEM, sizeof(*(MEM))) #endif /* __GNUC__ && !__STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/bits/midpoint.h b/libc/bits/midpoint.h index 6144cf837..aa013453f 100644 --- a/libc/bits/midpoint.h +++ b/libc/bits/midpoint.h @@ -3,8 +3,11 @@ #include "libc/assert.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) /** * Computes `(a + b) / 2` assuming unsigned. + * + * This implementation is the fastest on AMD Zen architecture. */ #define _midpoint(a, b) \ ({ \ @@ -18,6 +21,12 @@ : "r"(b_)); \ a_; \ }) +#else +/** + * Computes `(a + b) / 2` assuming unsigned. + */ +#define _midpoint(a, b) (((a) & (b)) + ((a) ^ (b)) / 2) +#endif /* __GNUC__ && !__STRICT_ANSI__ && x86 */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_BITS_MIDPOINT_H_ */ diff --git a/libc/calls/atfork.c b/libc/calls/atfork.c index 73ce94a0a..f7ff0b47b 100644 --- a/libc/calls/atfork.c +++ b/libc/calls/atfork.c @@ -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; } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 8303da205..d7cfd7455 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -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 *); diff --git a/libc/calls/ensurefds.c b/libc/calls/ensurefds.c index e036b8784..a089c18d1 100644 --- a/libc/calls/ensurefds.c +++ b/libc/calls/ensurefds.c @@ -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) { diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 56861a6cb..5498a6aea 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -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; } } diff --git a/libc/calls/g_sighandrvas.c b/libc/calls/g_sighandrvas.c index 26093c78f..ef14a7d0b 100644 --- a/libc/calls/g_sighandrvas.c +++ b/libc/calls/g_sighandrvas.c @@ -18,5 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +cthread_spinlock_t __sig_lock; unsigned __sighandrvas[NSIG]; unsigned __sighandflags[NSIG]; diff --git a/libc/calls/getconsolectrlevent.h b/libc/calls/getconsolectrlevent.internal.h similarity index 100% rename from libc/calls/getconsolectrlevent.h rename to libc/calls/getconsolectrlevent.internal.h diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 835e043e8..402698753 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -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 ─╬─│┼ diff --git a/libc/calls/ioctl_siocgifconf-nt.c b/libc/calls/ioctl_siocgifconf-nt.c index 1c62ce33b..d693df24c 100644 --- a/libc/calls/ioctl_siocgifconf-nt.c +++ b/libc/calls/ioctl_siocgifconf-nt.c @@ -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); } } diff --git a/libc/calls/kill-nt.c b/libc/calls/kill-nt.c index bb973803c..9dc6d46a0 100644 --- a/libc/calls/kill-nt.c +++ b/libc/calls/kill-nt.c @@ -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; + } } diff --git a/libc/calls/kill.c b/libc/calls/kill.c index 26b9f0c84..3fd6cd0d2 100644 --- a/libc/calls/kill.c +++ b/libc/calls/kill.c @@ -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; } diff --git a/libc/calls/mprotect.greg.c b/libc/calls/mprotect.greg.c index 7e0d78393..23de24917 100644 --- a/libc/calls/mprotect.greg.c +++ b/libc/calls/mprotect.greg.c @@ -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" diff --git a/libc/calls/mremap-sysv.greg.c b/libc/calls/mremap-sysv.greg.c index 83e5dd297..e0ea8371d 100644 --- a/libc/calls/mremap-sysv.greg.c +++ b/libc/calls/mremap-sysv.greg.c @@ -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" diff --git a/libc/calls/ntcontext2linux.c b/libc/calls/ntcontext2linux.c index b6c75be1b..ed7462f54 100644 --- a/libc/calls/ntcontext2linux.c +++ b/libc/calls/ntcontext2linux.c @@ -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)); +} diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c index 77f1a2378..bca10ac4a 100644 --- a/libc/calls/onntconsoleevent.c +++ b/libc/calls/onntconsoleevent.c @@ -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); diff --git a/libc/calls/pause.c b/libc/calls/pause.c index 514c787d9..b58303702 100644 --- a/libc/calls/pause.c +++ b/libc/calls/pause.c @@ -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. diff --git a/libc/calls/raise.c b/libc/calls/raise.c index 1635c6ab8..e4aa00b88 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -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; } diff --git a/libc/calls/releasefd.c b/libc/calls/releasefd.c index 56316ec6e..90e43ad57 100644 --- a/libc/calls/releasefd.c +++ b/libc/calls/releasefd.c @@ -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)); } } diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index d6b5ba473..edbaf99c6 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -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; } } diff --git a/libc/calls/samplepids.c b/libc/calls/samplepids.c index 7eb476183..a14712ebb 100644 --- a/libc/calls/samplepids.c +++ b/libc/calls/samplepids.c @@ -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)) { diff --git a/libc/calls/setpgid.c b/libc/calls/setpgid.c new file mode 100644 index 000000000..3bcb94c30 --- /dev/null +++ b/libc/calls/setpgid.c @@ -0,0 +1,58 @@ +/*-*- 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/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/nt/console.h" +#include "libc/sysv/errfuns.h" + +/** + * Changes process group for process. + */ +int setpgid(int pid, int pgid) { + int rc, me; + if (!IsWindows()) { + rc = sys_setpgid(pid, pgid); + } else { + 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; +} diff --git a/libc/calls/sig.c b/libc/calls/sig.c index a30c25bef..3836f8568 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -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(); + } } diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 0dc4a7875..f4b907d77 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -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) */ diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 8a129a331..fbabb0728 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -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); diff --git a/libc/calls/sigenter-freebsd.c b/libc/calls/sigenter-freebsd.c index ae627734c..30ccfcf37 100644 --- a/libc/calls/sigenter-freebsd.c +++ b/libc/calls/sigenter-freebsd.c @@ -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; diff --git a/libc/calls/signal.c b/libc/calls/signal.c index a90af2c7a..0bfbd5167 100644 --- a/libc/calls/signal.c +++ b/libc/calls/signal.c @@ -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; + } } diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index e80c31d06..aa0d00e89 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -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; diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c index 55d47c0c7..02bb3a083 100644 --- a/libc/calls/sigsuspend.c +++ b/libc/calls/sigsuspend.c @@ -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 diff --git a/libc/calls/struct/sigaction.h b/libc/calls/struct/sigaction.h index 9687d0caf..23bec2ee0 100644 --- a/libc/calls/struct/sigaction.h +++ b/libc/calls/struct/sigaction.h @@ -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 */ diff --git a/libc/calls/struct/ucontext-freebsd.internal.h b/libc/calls/struct/ucontext-freebsd.internal.h new file mode 100644 index 000000000..74e82c3ae --- /dev/null +++ b/libc/calls/struct/ucontext-freebsd.internal.h @@ -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_ */ diff --git a/libc/calls/unlink_s.c b/libc/calls/unlink_s.c index ec63d5758..562db3924 100644 --- a/libc/calls/unlink_s.c +++ b/libc/calls/unlink_s.c @@ -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. diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index f6c47c0a1..ed82d83ad 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -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; +} diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index 158e78ba3..b2ee12d60 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -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; } diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 3e98f3f3d..a7b9f5a1d 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -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(); } diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index f5cfc29ad..e6b99d4c2 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/reverse.internal.h" #include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" @@ -29,6 +28,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/asancodes.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" @@ -842,7 +842,7 @@ void *__asan_morgue_add(void *p) { for (;;) { i = __asan_morgue.i; j = (i + 1) & (ARRAYLEN(__asan_morgue.p) - 1); - if (cmpxchg(&__asan_morgue.i, i, j)) { + if (_lockcmpxchg(&__asan_morgue.i, i, j)) { r = __asan_morgue.p[i]; __asan_morgue.p[i] = p; return r; @@ -855,7 +855,7 @@ static void __asan_morgue_flush(void) { void *p; for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { p = __asan_morgue.p[i]; - if (cmpxchg(__asan_morgue.p + i, p, 0)) { + if (_lockcmpxchg(__asan_morgue.p + i, p, 0)) { if (weaken(dlfree)) { weaken(dlfree)(p); } @@ -1206,7 +1206,7 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) { } void __asan_report_load(uint8_t *addr, int size) { - if (cmpxchg(&__asan_noreentry, false, true)) { + if (_lockcmpxchg(&__asan_noreentry, false, true)) { if (!__vforked) { __asan_report_memory_fault(addr, size, "load")(); __asan_unreachable(); @@ -1219,7 +1219,7 @@ void __asan_report_load(uint8_t *addr, int size) { } void __asan_report_store(uint8_t *addr, int size) { - if (cmpxchg(&__asan_noreentry, false, true)) { + if (_lockcmpxchg(&__asan_noreentry, false, true)) { if (!__vforked) { __asan_report_memory_fault(addr, size, "store")(); __asan_unreachable(); @@ -1364,7 +1364,7 @@ static textstartup void __asan_shadow_existing_mappings(void) { textstartup void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) { static bool once; - if (!cmpxchg(&once, false, true)) return; + if (!_lockcmpxchg(&once, false, true)) return; if (IsWindows() && NtGetVersion() < kNtVersionWindows10) { __write_str("error: asan binaries require windows10\r\n"); __restorewintty(); diff --git a/libc/intrin/assertfail.c b/libc/intrin/assertfail.c index 27c9fcbae..6aaeec388 100644 --- a/libc/intrin/assertfail.c +++ b/libc/intrin/assertfail.c @@ -17,9 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/bits/weaken.h" +#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/log.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" @@ -29,17 +30,19 @@ */ relegated wontreturn void __assert_fail(const char *expr, const char *file, int line) { + int rc; static bool noreentry; kprintf("%s:%d: assert(%s) failed%n", file, line, expr); - if (cmpxchg(&noreentry, false, true)) { + if (_lockcmpxchg(&noreentry, false, true)) { if (weaken(__die)) { weaken(__die)(); } else { kprintf("can't backtrace b/c `__die` not linked%n"); } - __restorewintty(); - _Exit(23); + rc = 23; + } else { + rc = 24; } __restorewintty(); - _Exit(24); + _Exit(rc); } diff --git a/libc/bits/atomic_load.c b/libc/intrin/atomic_load.c similarity index 83% rename from libc/bits/atomic_load.c rename to libc/intrin/atomic_load.c index 9f2ef57a7..a384ce053 100644 --- a/libc/bits/atomic_load.c +++ b/libc/intrin/atomic_load.c @@ -16,15 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/macros.internal.h" -#include "libc/str/str.h" +#include "libc/intrin/atomic_load.h" /** - * Atomically loads value. + * Reads scalar from memory w/ one operation. * * This macro is intended to prevent things like compiler load tearing * optimizations. + * + * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @return *(MEM) + * @note defeats compiler load tearing optimizations + * @note alignas(𝑘) is implied if compiler knows type + * @note alignas(𝑘) only avoids multi-core / cross-page edge cases + * @see Intel's Six-Thousand Page Manual V.3A §8.2.3.1 + * @see atomic_store() */ intptr_t(atomic_load)(void *p, size_t n) { intptr_t x = 0; diff --git a/libc/intrin/atomic_load.h b/libc/intrin/atomic_load.h new file mode 100644 index 000000000..d5a292de9 --- /dev/null +++ b/libc/intrin/atomic_load.h @@ -0,0 +1,22 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ +#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +intptr_t atomic_load(void *, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define atomic_load(MEM) \ + ({ \ + autotype(MEM) Mem = (MEM); \ + typeof(*Mem) Reg; \ + asm("mov\t%1,%0" : "=r"(Reg) : "m"(*Mem)); \ + Reg; \ + }) +#else +#define atomic_load(MEM) atomic_load(MEM, sizeof(*(MEM))) +#endif /* GNUC && !ANSI && x86 */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_LOAD_H_ */ diff --git a/libc/bits/atomic_store.c b/libc/intrin/atomic_store.c similarity index 75% rename from libc/bits/atomic_store.c rename to libc/intrin/atomic_store.c index 74ddf68dd..c4c32e6b7 100644 --- a/libc/bits/atomic_store.c +++ b/libc/intrin/atomic_store.c @@ -16,15 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/macros.internal.h" -#include "libc/str/str.h" +#include "libc/intrin/atomic_store.h" /** - * Atomically stores value. + * Saves scalar to memory w/ one operation. * - * This macro is intended to prevent things like compiler store tearing - * optimizations. + * This is guaranteed to happen in either one or zero operations, + * depending on whether or not it's possible for *(MEM) to be read + * afterwards. This macro only forbids compiler from using >1 ops. + * + * @param MEM is alignas(𝑘) uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param VAL is uint𝑘_t w/ better encoding for immediates (constexpr) + * @return VAL + * @note alignas(𝑘) on nexgen32e only needed for end of page gotcha + * @note alignas(𝑘) is implied if compiler knows type + * @note needed to defeat store tearing optimizations + * @see Intel Six-Thousand Page Manual Manual V.3A §8.2.3.1 + * @see atomic_load() */ intptr_t(atomic_store)(void *p, intptr_t x, size_t n) { switch (n) { diff --git a/libc/intrin/atomic_store.h b/libc/intrin/atomic_store.h new file mode 100644 index 000000000..9a83c407c --- /dev/null +++ b/libc/intrin/atomic_store.h @@ -0,0 +1,23 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ +#define COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +intptr_t atomic_store(void *, intptr_t, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define atomic_store(MEM, VAL) \ + ({ \ + autotype(VAL) Val = (VAL); \ + typeof(&Val) Mem = (MEM); \ + asm("mov%z1\t%1,%0" : "=m"(*Mem) : "r"(Val)); \ + Val; \ + }) +#else +#define atomic_store(MEM, VAL) \ + atomic_store(MEM, VAL, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAL)))) +#endif /* __GNUC__ && !__STRICT_ANSI__ */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_ATOMIC_STORE_H_ */ diff --git a/libc/bits/cmpxchg.c b/libc/intrin/cmpxchg.c similarity index 77% rename from libc/bits/cmpxchg.c rename to libc/intrin/cmpxchg.c index 8800d7c5d..f8e363138 100644 --- a/libc/bits/cmpxchg.c +++ b/libc/intrin/cmpxchg.c @@ -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" /** * Compares and exchanges. @@ -24,23 +24,23 @@ * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} * @param size is automatically supplied by macro wrapper * @return true if value was exchanged, otherwise false - * @see lockcmpxchg() + * @see _lockcmpxchg() */ -bool(cmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, - size_t size) { +bool(_cmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, + size_t size) { switch (size) { case 1: - return cmpxchg((int8_t *)ifthing, (int8_t)isequaltome, - (int8_t)replaceitwithme); + return _cmpxchg((int8_t *)ifthing, (int8_t)isequaltome, + (int8_t)replaceitwithme); case 2: - return cmpxchg((int16_t *)ifthing, (int16_t)isequaltome, - (int16_t)replaceitwithme); + return _cmpxchg((int16_t *)ifthing, (int16_t)isequaltome, + (int16_t)replaceitwithme); case 4: - return cmpxchg((int32_t *)ifthing, (int32_t)isequaltome, - (int32_t)replaceitwithme); + return _cmpxchg((int32_t *)ifthing, (int32_t)isequaltome, + (int32_t)replaceitwithme); case 8: - return cmpxchg((int64_t *)ifthing, (int64_t)isequaltome, - (int64_t)replaceitwithme); + return _cmpxchg((int64_t *)ifthing, (int64_t)isequaltome, + (int64_t)replaceitwithme); default: return false; } diff --git a/libc/intrin/cmpxchg.h b/libc/intrin/cmpxchg.h new file mode 100644 index 000000000..2f560c813 --- /dev/null +++ b/libc/intrin/cmpxchg.h @@ -0,0 +1,29 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_ +#define COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_ +#include "libc/bits/asmflag.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +bool _cmpxchg(void *, intptr_t, intptr_t, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) +#define _cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ + ({ \ + bool DidIt; \ + autotype(IFTHING) IfThing = (IFTHING); \ + typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ + typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ + asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \ + : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ + : "r"(ReplaceItWithMe) \ + : "cc"); \ + DidIt; \ + }) +#else +#define _cmpxchg(MEM, CMP, VAL) \ + _cmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) +#endif /* GNUC && !ANSI && x86 */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_CMPXCHG_H_ */ diff --git a/libc/bits/cmpxchg16b.internal.h b/libc/intrin/cmpxchg16b.internal.h similarity index 97% rename from libc/bits/cmpxchg16b.internal.h rename to libc/intrin/cmpxchg16b.internal.h index 04fd3223d..a254d9fba 100644 --- a/libc/bits/cmpxchg16b.internal.h +++ b/libc/intrin/cmpxchg16b.internal.h @@ -1,6 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_ #define COSMOPOLITAN_LIBC_BITS_CMPXCHG16B_INTERNAL_H_ -#include "libc/bits/bits.h" +#include "libc/bits/asmflag.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index e679a79fb..4c56809ed 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -16,6 +16,8 @@ const char *DescribeNtFileFlagsAndAttributes(uint32_t); const char *DescribeNtFileShareFlags(uint32_t); const char *DescribeNtFileAccessFlags(uint32_t); const char *DescribeNtProcessAccessFlags(uint32_t); +const char *DescribeNtConsoleModeInputFlags(uint32_t); +const char *DescribeNtConsoleModeOutputFlags(uint32_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/describentconsolemodeinputflags.greg.c b/libc/intrin/describentconsolemodeinputflags.greg.c new file mode 100644 index 000000000..0203679bc --- /dev/null +++ b/libc/intrin/describentconsolemodeinputflags.greg.c @@ -0,0 +1,41 @@ +/*-*- 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/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/consolemodeflags.h" + +static const struct DescribeFlags kConsoleModeInputFlags[] = { + {kNtEnableProcessedInput, "ProcessedInput"}, // + {kNtEnableLineInput, "LineInput"}, // + {kNtEnableEchoInput, "EchoInput"}, // + {kNtEnableWindowInput, "WindowInput"}, // + {kNtEnableMouseInput, "MouseInput"}, // + {kNtEnableInsertMode, "InsertMode"}, // + {kNtEnableQuickEditMode, "QuickEditMode"}, // + {kNtEnableExtendedFlags, "ExtendedFlags"}, // + {kNtEnableAutoPosition, "AutoPosition"}, // + {kNtEnableVirtualTerminalInput, "VirtualTerminalInput"}, // +}; + +const char *DescribeNtConsoleModeInputFlags(uint32_t x) { + static char consolemodeinputflags[256]; + return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags), + kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags), + "kNtEnable", x); +} diff --git a/libc/runtime/abort.S b/libc/intrin/describentconsolemodeoutputflags.greg.c similarity index 56% rename from libc/runtime/abort.S rename to libc/intrin/describentconsolemodeoutputflags.greg.c index 2d7bf3e08..b862efae4 100644 --- a/libc/runtime/abort.S +++ b/libc/intrin/describentconsolemodeoutputflags.greg.c @@ -1,7 +1,7 @@ -/*-*- 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│ +/*-*- 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 │ @@ -16,46 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/runtime/internal.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/nr.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/macros.internal.h" -.privileged +#include "libc/nt/enum/consolemodeflags.h" -// Terminates program abnormally. -// -// This function first tries to trigger your SIGABRT handler. If -// there isn't one or execution resumes, then abort() terminates -// the program using an escalating variety methods of increasing -// brutality. -// -// @forcealignargpointer -// @asyncsignalsafe -// @noreturn -abort: push %rbp - mov %rsp,%rbp - and $-16,%rsp - sub $16,%rsp -#if SupportsWindows() - testb IsWindows() - jnz sys_abort_nt -#endif - mov SIG_SETMASK,%edi - mov %rsp,%rsi - push $0xffffffffffffffdf # all bits blocked but SIGABRT - push $0xffffffffffffffff # assumes von neum. arithmetic - pop 8(%rsi) - pop (%rsi) - xor %edx,%edx # don't care about old sigmask - pushpop 4*4,%r10 # sizeof(sigset_t) for systemd - mov __NR_sigprocmask,%eax # sys_sigprocmask is hookable - syscall - mov __NR_getpid,%eax - syscall - mov %eax,%edi - mov SIGABRT,%esi - mov __NR_kill,%eax - syscall # avoid hook and less bt noise - call _Exit - .endfn abort,globl,protected +static const struct DescribeFlags kConsoleModeOutputFlags[] = { + {kNtEnableProcessedOutput, "EnableProcessedOutput"}, // + {kNtEnableWrapAtEolOutput, "EnableWrapAtEolOutput"}, // + {kNtEnableVirtualTerminalProcessing, "EnableVirtualTerminalProcessing"}, // + {kNtDisableNewlineAutoReturn, "DisableNewlineAutoReturn"}, // + {kNtEnableLvbGridWorldwide, "EnableLvbGridWorldwide"}, // +}; + +const char *DescribeNtConsoleModeOutputFlags(uint32_t x) { + static char consolemodeoutputflags[128]; + return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags), + kConsoleModeOutputFlags, + ARRAYLEN(kConsoleModeOutputFlags), "kNt", x); +} diff --git a/libc/intrin/describentprocessaccessflags.greg.c b/libc/intrin/describentprocessaccessflags.greg.c index 16faf576f..3c93b5cc6 100644 --- a/libc/intrin/describentprocessaccessflags.greg.c +++ b/libc/intrin/describentprocessaccessflags.greg.c @@ -21,20 +21,20 @@ #include "libc/nt/enum/processaccess.h" static const struct DescribeFlags kProcessAccessflags[] = { - {kNtProcessAllAccess, "AllAccess"}, - {kNtProcessCreateProcess, "CreateProcess"}, - {kNtProcessCreateThread, "CreateThread"}, - {kNtProcessDupHandle, "DupHandle"}, - {kNtProcessQueryInformation, "QueryInformation"}, - {kNtProcessQueryLimitedInformation, "QueryLimitedInformation"}, - {kNtProcessSetInformation, "SetInformation"}, - {kNtProcessSetQuota, "SetQuota"}, - {kNtProcessSuspendResume, "SuspendResume"}, - {kNtProcessTerminate, "Terminate"}, - {kNtProcessVmOperation, "VmOperation"}, - {kNtProcessVmRead, "VmRead"}, - {kNtProcessVmWrite, "VmWrite"}, - {kNtProcessSynchronize, "Synchronize"}, + {kNtProcessAllAccess, "AllAccess"}, // + {kNtProcessCreateProcess, "CreateProcess"}, // + {kNtProcessCreateThread, "CreateThread"}, // + {kNtProcessDupHandle, "DupHandle"}, // + {kNtProcessQueryInformation, "QueryInformation"}, // + {kNtProcessQueryLimitedInformation, "QueryLimitedInformation"}, // + {kNtProcessSetInformation, "SetInformation"}, // + {kNtProcessSetQuota, "SetQuota"}, // + {kNtProcessSuspendResume, "SuspendResume"}, // + {kNtProcessTerminate, "Terminate"}, // + {kNtProcessVmOperation, "VmOperation"}, // + {kNtProcessVmRead, "VmRead"}, // + {kNtProcessVmWrite, "VmWrite"}, // + {kNtProcessSynchronize, "Synchronize"}, // }; const char *DescribeNtProcessAccessFlags(uint32_t x) { diff --git a/libc/calls/getpid.c b/libc/intrin/getpid.c similarity index 100% rename from libc/calls/getpid.c rename to libc/intrin/getpid.c diff --git a/test/dsp/core/float2short_test.c b/libc/intrin/gettid.c similarity index 54% rename from test/dsp/core/float2short_test.c rename to libc/intrin/gettid.c index 0d229565a..3e6b16f1b 100644 --- a/test/dsp/core/float2short_test.c +++ b/libc/intrin/gettid.c @@ -16,68 +16,63 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "dsp/core/core.h" -#include "dsp/mpeg/mpeg.h" -#include "libc/log/check.h" -#include "libc/macros.internal.h" -#include "libc/rand/rand.h" -#include "libc/runtime/buffer.h" -#include "libc/testlib/ezbench.h" -#include "libc/testlib/testlib.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/nt/thread.h" -short pcm[8][8]; -float binary32[8][8]; -struct GuardedBuffer b1, b2; +/** + * Returns current thread id. + * @asyncsignalsafe + */ +int gettid(void) { + int rc; + int64_t wut; -TEST(float2short, test) { - binary32[0][0] = -0.5; - binary32[0][1] = 0.0; - binary32[0][2] = 0.5; - float2short(8, pcm, binary32); - EXPECT_EQ(-16384, pcm[0][0]); - EXPECT_EQ(0, pcm[0][1]); - EXPECT_EQ(16384, pcm[0][2]); -} - -TEST(float2short, testOverflow) { - binary32[0][0] = -1.1; - binary32[0][1] = -1.0; - binary32[0][2] = 1.0; - binary32[0][3] = 1.1; - float2short(8, pcm, binary32); - EXPECT_EQ(-32768, pcm[0][0]); - EXPECT_EQ(-32768, pcm[0][1]); - EXPECT_EQ(32767, pcm[0][2]); - EXPECT_EQ(32767, pcm[0][3]); -} - -void unclamped(size_t n, short pcm16[n][8], const float binary32[n][8]) { - size_t i, j; - for (i = 0; i < n; ++i) { - for (j = 0; j < 8; ++j) { - pcm16[i][j] = binary32[i][j] * 32768; - } + if (IsLinux()) { + asm("syscall" + : "=a"(rc) // man says always succeeds + : "0"(186) // __NR_gettid + : "rcx", "r11", "memory"); + return rc; } -} -plm_samples_t samps; - -void randomizeaudio(void) { - size_t i; - for (i = 0; i < ARRAYLEN(samps.interleaved); ++i) { - samps.interleaved[i] = randf(); + if (IsXnu()) { + asm("syscall" // xnu/osfmk/kern/ipc_tt.c + : "=a"(rc) // assume success + : "0"(0x1000000 | 27) // Mach thread_self_trap() + : "rcx", "r11", "memory", "cc"); + return rc; } -} -void float2short_pure(void) { - float2short(ARRAYLEN(samps.interleaved) / 8, pcm, (void *)samps.interleaved); -} + if (IsOpenbsd()) { + asm("syscall" + : "=a"(rc) // man says always succeeds + : "0"(299) // getthrid() + : "rcx", "r11", "memory", "cc"); + return rc; + } -void float2short_unclamped(void) { - unclamped(ARRAYLEN(samps.interleaved) / 8, pcm, (void *)samps.interleaved); -} + if (IsNetbsd()) { + asm("syscall" + : "=a"(rc) // man says always succeeds + : "0"(311) // _lwp_self() + : "rcx", "r11", "memory", "cc"); + return rc; + } -BENCH(float2short, audioframe) { - EZBENCH(randomizeaudio(), float2short_pure()); - EZBENCH(randomizeaudio(), float2short_unclamped()); + if (IsFreebsd()) { + asm("syscall" + : "=a"(rc), // only fails w/ EFAULT, which isn't possible + "=m"(wut) // must be 64-bit + : "0"(432), // thr_self() + "D"(&wut) // but not actually 64-bit + : "rcx", "r11", "memory", "cc"); + return wut; // narrowing intentional + } + + if (IsWindows()) { + return GetCurrentThreadId(); + } + + return getpid(); } diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 4b5d0dce8..afb2629d9 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -26,6 +26,7 @@ LIBC_INTRIN_A_CHECKS = \ LIBC_INTRIN_A_DIRECTDEPS = \ LIBC_STUBS \ LIBC_SYSV \ + LIBC_SYSV_CALLS \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 @@ -66,6 +67,7 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \ o/$(MODE)/libc/intrin/createfile.greg.o \ o/$(MODE)/libc/intrin/createpipe.greg.o \ o/$(MODE)/libc/intrin/closehandle.greg.o \ +o/$(MODE)/libc/intrin/openprocess.greg.o \ o/$(MODE)/libc/intrin/createthread.greg.o \ o/$(MODE)/libc/intrin/createprocess.greg.o \ o/$(MODE)/libc/intrin/describeflags.greg.o \ @@ -75,9 +77,11 @@ o/$(MODE)/libc/intrin/flushviewoffile.greg.o \ o/$(MODE)/libc/intrin/deviceiocontrol.greg.o \ o/$(MODE)/libc/intrin/createdirectory.greg.o \ o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \ +o/$(MODE)/libc/intrin/terminateprocess.greg.o \ o/$(MODE)/libc/intrin/getfileattributes.greg.o \ o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \ o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \ +o/$(MODE)/libc/intrin/generateconsolectrlevent.greg.o \ o/$(MODE)/libc/intrin/kstarttsc.o \ o/$(MODE)/libc/intrin/nomultics.o \ o/$(MODE)/libc/intrin/ntconsolemode.o: \ diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index e471f752a..ed95a040d 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ShouldUseMsabiAttribute() 1 -#include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" @@ -27,7 +26,10 @@ #include "libc/errno.h" #include "libc/fmt/divmod10.internal.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" +#include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" @@ -42,6 +44,7 @@ #include "libc/str/utf16.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/prot.h" +#include "libc/time/clockstonanos.internal.h" struct Timestamps { unsigned long long birth; @@ -51,14 +54,7 @@ struct Timestamps { extern int __pid; extern bool __replmode; extern bool __nomultics; -volatile unsigned long long __kbirth; - -static inline uint64_t ClocksToNanos(uint64_t x, uint64_t y) { - // approximation of round(x*.323018) which is usually - // the ratio between inva rdtsc ticks and nanoseconds - uint128_t difference = x - y; - return (difference * 338709) >> 20; -} +unsigned long long __kbirth; // see fork-nt.c privileged static struct Timestamps kenter(void) { struct Timestamps ts; @@ -67,7 +63,7 @@ privileged static struct Timestamps kenter(void) { if (!ts.birth) { ts.birth = kStartTsc; if (!ts.birth) ts.birth = 1; - cmpxchg(&__kbirth, 0, ts.birth); + _lockcmpxchg(&__kbirth, 0, ts.birth); } return ts; } @@ -78,7 +74,9 @@ privileged static void kleave(struct Timestamps ts) { elapse = unsignedsubtract(finish, ts.start); adjust = ts.birth + elapse; if (!adjust) adjust = 1; - cmpxchg(&__kbirth, ts.birth, adjust); /* ignore overlapping time intervals */ + if (__kbirth == ts.birth) { + _lockcmpxchg(&__kbirth, ts.birth, adjust); // ignore overlapping intervals + } } privileged static inline char *kadvance(char *p, char *e, long n) { @@ -732,6 +730,32 @@ privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) { return m; } +/** + * Privileged snprintf() w/o timestamp feature. + * + * This provides a marginal performance boost, but it means %T can no + * longer be used. + * + * snprintf(".") l: 25𝑐 8𝑛𝑠 + * kusnprintf(".") l: 22𝑐 7𝑛𝑠 + * ksnprintf(".") l: 54𝑐 17𝑛𝑠 + * + * @param b is buffer, and guaranteed a NUL-terminator if `n>0` + * @param n is number of bytes available in buffer + * @return length of output excluding NUL, which may exceed `n` + * @see kprintf() for documentation + * @asyncsignalsafe + * @vforksafe + */ +privileged size_t kusnprintf(char *b, size_t n, const char *fmt, ...) { + size_t m; + va_list v; + va_start(v, fmt); + m = kformat(b, n, fmt, v, (struct Timestamps){0}); + va_end(v); + return m; +} + /** * Privileged vsnprintf(). * diff --git a/libc/intrin/kprintf.h b/libc/intrin/kprintf.h index 5bc910675..7fb1450db 100644 --- a/libc/intrin/kprintf.h +++ b/libc/intrin/kprintf.h @@ -5,6 +5,7 @@ COSMOPOLITAN_C_START_ void kprintf(const char *, ...); size_t ksnprintf(char *, size_t, const char *, ...); +size_t kusnprintf(char *, size_t, const char *, ...); void kvprintf(const char *, va_list); size_t kvsnprintf(char *, size_t, const char *, va_list); bool kisdangerous(const void *); diff --git a/libc/bits/lockcmpxchg.c b/libc/intrin/lockcmpxchg.c similarity index 76% rename from libc/bits/lockcmpxchg.c rename to libc/intrin/lockcmpxchg.c index 59121e835..126659419 100644 --- a/libc/bits/lockcmpxchg.c +++ b/libc/intrin/lockcmpxchg.c @@ -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/lockcmpxchg.h" /** * Compares and exchanges w/ lock prefix. @@ -24,23 +24,23 @@ * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} * @param size is automatically supplied by macro wrapper * @return true if value was exchanged, otherwise false - * @see cmpxchg() + * @see cmpxchg() if only written by one thread */ -bool(lockcmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, - size_t size) { +bool(_lockcmpxchg)(void *ifthing, intptr_t isequaltome, + intptr_t replaceitwithme, size_t size) { switch (size) { case 1: - return lockcmpxchg((int8_t *)ifthing, (int8_t)isequaltome, - (int8_t)replaceitwithme); + return _lockcmpxchg((int8_t *)ifthing, (int8_t)isequaltome, + (int8_t)replaceitwithme); case 2: - return lockcmpxchg((int16_t *)ifthing, (int16_t)isequaltome, - (int16_t)replaceitwithme); + return _lockcmpxchg((int16_t *)ifthing, (int16_t)isequaltome, + (int16_t)replaceitwithme); case 4: - return lockcmpxchg((int32_t *)ifthing, (int32_t)isequaltome, - (int32_t)replaceitwithme); + return _lockcmpxchg((int32_t *)ifthing, (int32_t)isequaltome, + (int32_t)replaceitwithme); case 8: - return lockcmpxchg((int64_t *)ifthing, (int64_t)isequaltome, - (int64_t)replaceitwithme); + return _lockcmpxchg((int64_t *)ifthing, (int64_t)isequaltome, + (int64_t)replaceitwithme); default: return false; } diff --git a/libc/intrin/lockcmpxchg.h b/libc/intrin/lockcmpxchg.h new file mode 100644 index 000000000..8079c6309 --- /dev/null +++ b/libc/intrin/lockcmpxchg.h @@ -0,0 +1,29 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_ +#define COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_ +#include "libc/bits/asmflag.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +bool _lockcmpxchg(void *, intptr_t, intptr_t, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) +#define _lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ + ({ \ + bool DidIt; \ + autotype(IFTHING) IfThing = (IFTHING); \ + typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ + typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ + asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \ + : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ + : "r"(ReplaceItWithMe) \ + : "cc"); \ + DidIt; \ + }) +#else +#define _lockcmpxchg(MEM, CMP, VAL) \ + _lockcmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) +#endif /* GNUC && !ANSI && x86 */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKCMPXCHG_H_ */ diff --git a/libc/bits/lockcmpxchg16b.internal.h b/libc/intrin/lockcmpxchg16b.h similarity index 79% rename from libc/bits/lockcmpxchg16b.internal.h rename to libc/intrin/lockcmpxchg16b.h index aa6875632..3a6196099 100644 --- a/libc/bits/lockcmpxchg16b.internal.h +++ b/libc/intrin/lockcmpxchg16b.h @@ -1,9 +1,10 @@ -#ifndef COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ -#include "libc/bits/bits.h" +#ifndef COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_ +#define COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_ +#include "libc/bits/asmflag.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) /** * Compares and exchanges 128-bit value, i.e. * @@ -38,7 +39,8 @@ static inline bool _lockcmpxchg16b(uint128_t *IfThing, uint128_t *IsEqualToMe, } return DidIt; } +#endif /* __GNUC__ && !__STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_INTERNAL_H_ */ +#endif /* COSMOPOLITAN_LIBC_BITS_LOCKCMPXCHG16B_H_ */ diff --git a/libc/rand/randf.c b/libc/intrin/lockxadd.c similarity index 65% rename from libc/rand/randf.c rename to libc/intrin/lockxadd.c index 7bb29080c..653a6377d 100644 --- a/libc/rand/randf.c +++ b/libc/intrin/lockxadd.c @@ -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 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 │ @@ -16,12 +16,29 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/limits.h" -#include "libc/rand/internal.h" -#include "libc/rand/lcg.internal.h" -#include "libc/rand/rand.h" +#include "libc/intrin/lockxadd.h" +#include "libc/runtime/runtime.h" -float randf(void) { - return (double)(int)(KnuthLinearCongruentialGenerator(&g_rando) >> 32) / - INT_MAX; +/** + * Compares and exchanges w/ lock prefix. + * + * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param size is automatically supplied by macro wrapper + * @return value at location `*ifthing` *before* addition + * @see InterlockedAdd() for a very similar API + * @see xadd() if only written by one thread + */ +intptr_t(_lockxadd)(void *ifthing, intptr_t replaceitwithme, size_t size) { + switch (size) { + case 1: + return _lockxadd((int8_t *)ifthing, (int8_t)replaceitwithme); + case 2: + return _lockxadd((int16_t *)ifthing, (int16_t)replaceitwithme); + case 4: + return _lockxadd((int32_t *)ifthing, (int32_t)replaceitwithme); + case 8: + return _lockxadd((int64_t *)ifthing, (int64_t)replaceitwithme); + default: + abort(); + } } diff --git a/libc/bits/lockxadd.internal.h b/libc/intrin/lockxadd.h similarity index 61% rename from libc/bits/lockxadd.internal.h rename to libc/intrin/lockxadd.h index 544f2f2cf..b47c53f39 100644 --- a/libc/bits/lockxadd.internal.h +++ b/libc/intrin/lockxadd.h @@ -1,8 +1,11 @@ -#ifndef COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ +#ifndef COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_ +#define COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +intptr_t _lockxadd(void *, intptr_t, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) #define _lockxadd(PTR, VAL) \ ({ \ typeof(*(PTR)) Res; \ @@ -10,7 +13,10 @@ COSMOPOLITAN_C_START_ asm volatile("lock xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \ Res; /* contains *PTR before addition cf. InterlockedAdd() */ \ }) +#else +#define _lockxadd(MEM, VAL) _lockxadd(MEM, (intptr_t)(VAL), sizeof(*(MEM))) +#endif /* GNUC && !ANSI && x86 */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_BITS_LOCKXADD_INTERNAL_H_ */ +#endif /* COSMOPOLITAN_LIBC_BITS_LOCKXADD_H_ */ diff --git a/libc/bits/lockxchg.c b/libc/intrin/lockxchg.c similarity index 98% rename from libc/bits/lockxchg.c rename to libc/intrin/lockxchg.c index 651e23052..a372ff997 100644 --- a/libc/bits/lockxchg.c +++ b/libc/intrin/lockxchg.c @@ -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/lockxchg.h" /** * Compares and exchanges w/ lock prefix. diff --git a/libc/intrin/lockxchg.h b/libc/intrin/lockxchg.h new file mode 100644 index 000000000..72733a645 --- /dev/null +++ b/libc/intrin/lockxchg.h @@ -0,0 +1,29 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_ +#define COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +intptr_t lockxchg(void *, void *, size_t); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/** + * Exchanges *MEMORY into *LOCALVAR w/ one operation. + * + * @param MEMORY is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param LOCALVAR is uint𝑘_t[hasatleast 1] + * @return LOCALVAR[0] + * @see xchg() + */ +#define lockxchg(MEMORY, LOCALVAR) \ + ({ \ + asm("xchg\t%0,%1" : "+%m"(*(MEMORY)), "+r"(*(LOCALVAR))); \ + *(LOCALVAR); \ + }) +#else +#define lockxchg(MEM, VAR) \ + lockxchg(MEM, VAR, sizeof(*(MEM)) / (sizeof(*(MEM)) == sizeof(*(VAR)))) +#endif /* __GNUC__ && !__STRICT_ANSI__ */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKXCHG_H_ */ diff --git a/libc/intrin/ntconsolemode.c b/libc/intrin/ntconsolemode.c index f25cb4a99..8c72a48ed 100644 --- a/libc/intrin/ntconsolemode.c +++ b/libc/intrin/ntconsolemode.c @@ -18,4 +18,4 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/internal.h" -uint32_t __ntconsolemode[2]; +uint32_t __ntconsolemode[3]; diff --git a/libc/intrin/restorewintty.greg.c b/libc/intrin/restorewintty.greg.c index 0d31825d4..456562e60 100644 --- a/libc/intrin/restorewintty.greg.c +++ b/libc/intrin/restorewintty.greg.c @@ -23,7 +23,12 @@ #include "libc/runtime/internal.h" uint32_t __winmainpid; -const char kConsoleHandles[2] = {kNtStdInputHandle, kNtStdOutputHandle}; + +const char kConsoleHandles[3] = { + kNtStdInputHandle, + kNtStdOutputHandle, + kNtStdErrorHandle, +}; /** * Puts cmd.exe gui back the way it was. @@ -31,7 +36,7 @@ const char kConsoleHandles[2] = {kNtStdInputHandle, kNtStdOutputHandle}; noasan void __restorewintty(void) { int i; if (IsWindows() && GetCurrentProcessId() == __winmainpid) { - for (i = 0; i < 2; ++i) { + for (i = 0; i < 3; ++i) { SetConsoleMode(GetStdHandle(kConsoleHandles[i]), __ntconsolemode[i]); } __winmainpid = 0; diff --git a/libc/calls/gettid.c b/libc/intrin/spinlock.c similarity index 72% rename from libc/calls/gettid.c rename to libc/intrin/spinlock.c index 001718807..0ee680400 100644 --- a/libc/calls/gettid.c +++ b/libc/intrin/spinlock.c @@ -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 │ @@ -16,24 +16,35 @@ │ 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/internal.h" -#include "libc/dce.h" -#include "libc/nt/thread.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/lockcmpxchg.h" +#include "libc/intrin/spinlock.h" /** - * Returns current thread id. - * @asyncsignalsafe + * Acquires spinlock. */ -uint32_t gettid(void) { - uint32_t res; - if (!IsWindows()) { - res = sys_gettid(); - if (res <= 0) { - res = getpid(); - } - return res; - } else { - return GetCurrentThreadId(); +void cthread_spinlock(cthread_spinlock_t *lock) { +#if 0 + // TODO(jart): possibly reenable for !NDEBUG when we have TLS + int me = gettid(); + if (lock->x && lock->owner == me) { + assert(!"cosmo spinlock not intended to be reentrant"); + return; } +#endif + do + while (lock->x) asm("pause"); + while (!_lockcmpxchg(&lock->x, false, true)); +#if 0 + lock->owner = me; +#endif +} + +/** + * Releases spinlock. + */ +void cthread_spunlock(cthread_spinlock_t *lock) { + lock->x = false; } diff --git a/libc/intrin/spinlock.h b/libc/intrin/spinlock.h new file mode 100644 index 000000000..56ce593c7 --- /dev/null +++ b/libc/intrin/spinlock.h @@ -0,0 +1,22 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ +#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/* "Place each synchronization variable alone, + separated by 128 bytes or in a separate cache line." + ──Intel Optimization Manual §8.3.1 */ +struct cthread_spinlock_t { + bool x; + int owner; + char __ignore[128 - 1 - 4]; +} forcealign(128); + +typedef struct cthread_spinlock_t cthread_spinlock_t; + +void cthread_spinlock(cthread_spinlock_t *) dontthrow; +void cthread_spunlock(cthread_spinlock_t *) dontthrow; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */ diff --git a/libc/log/die.c b/libc/log/die.c index 647825420..9c2ba9f1e 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -16,9 +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/dce.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" @@ -33,18 +32,18 @@ */ relegated wontreturn void __die(void) { /* asan runtime depends on this function */ + int rc; static bool once; - kprintf("__die() called%n"); - if (lockcmpxchg(&once, false, true)) { + if (_lockcmpxchg(&once, false, true)) { __restore_tty(1); if (IsDebuggerPresent(false)) { DebugBreak(); } ShowBacktrace(2, NULL); - __restorewintty(); - _Exit(77); + rc = 77; + } else { + rc = 78; } - kprintf("panic: __die() died%n"); __restorewintty(); - _Exit(78); + _Exit(rc); } diff --git a/libc/log/leaks.c b/libc/log/leaks.c index cb7658372..1f08ac202 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -20,6 +20,7 @@ #include "libc/calls/strace.internal.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" @@ -72,7 +73,7 @@ static noasan bool HasLeaks(void) { */ noasan void CheckForMemoryLeaks(void) { struct mallinfo mi; - if (!lockcmpxchg(&once, false, true)) { + if (!_lockcmpxchg(&once, false, true)) { kprintf("CheckForMemoryLeaks() may only be called once\n"); exit(1); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 44c8f578a..36f3d665b 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -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/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/calls/sigbits.h" @@ -25,6 +24,7 @@ #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/backtrace.internal.h" #include "libc/log/gdb.h" #include "libc/log/internal.h" @@ -301,7 +301,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, static bool noreentry, notpossible; st = __strace, __strace = 0; ft = g_ftrace, g_ftrace = 0; - if (lockcmpxchg(&noreentry, false, true)) { + if (_lockcmpxchg(&noreentry, false, true)) { if (!__vforked) { rip = ctx ? ctx->uc_mcontext.rip : 0; err = errno; @@ -331,7 +331,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, g_ftrace = ft; __strace = st; return; - } else if (lockcmpxchg(¬possible, false, true)) { + } else if (_lockcmpxchg(¬possible, false, true)) { __minicrash(sig, si, ctx, "WHILE CRASHING"); } else { for (;;) { diff --git a/libc/nexgen32e/rdtscp.h b/libc/nexgen32e/rdtscp.h index 92b1a2ad8..4c5ecbbd2 100644 --- a/libc/nexgen32e/rdtscp.h +++ b/libc/nexgen32e/rdtscp.h @@ -1,6 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_ #define COSMOPOLITAN_LIBC_NEXGEN32E_RDTSCP_H_ -#include "libc/bits/bits.h" +#include "libc/bits/asmflag.h" #include "libc/nexgen32e/x86feature.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/rand/rand.h b/libc/rand/rand.h index 674cc8acc..48cf0824c 100644 --- a/libc/rand/rand.h +++ b/libc/rand/rand.h @@ -17,7 +17,6 @@ void rt_end(double *, double *, double *, double *, double *); char *strfry(char *); int getentropy(void *, size_t); ssize_t getrandom(void *, size_t, unsigned); -float randf(void); char *initstate(unsigned, char *, size_t); char *setstate(char *); long random(void); diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index b7f6c3ebd..87acdd1de 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -18,11 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/bits.h" -#include "libc/bits/lockcmpxchg16b.internal.h" -#include "libc/bits/lockxadd.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nt/thread.h" #include "libc/rand/rand.h" @@ -32,8 +30,8 @@ extern int __pid; static int thepid; -static int thecount; static uint128_t thepool; +static cthread_spinlock_t rand64_lock; /** * Returns nondeterministic random data. @@ -48,48 +46,35 @@ static uint128_t thepool; * @see rdseed(), rdrand(), rand(), random(), rngset() * @note this function is not intended for cryptography * @note this function passes bigcrush and practrand - * @note this function takes at minimum 30 cycles + * @note this function takes at minimum 15 cycles * @asyncsignalsafe * @threadsafe * @vforksafe */ uint64_t rand64(void) { - void *d; - int c1, p1, p2; - uint128_t s1, s2; - do { - p1 = __pid; - p2 = thepid; - c1 = thecount; - asm volatile("" ::: "memory"); - s1 = thepool; - if (p1 == p2) { - // fast path - s2 = s1; - } else { - // slow path - if (!p2) { - // first call so get some cheap entropy - if ((d = (void *)getauxval(AT_RANDOM))) { - memcpy(&s2, d, 16); // kernel entropy - } else { - s2 = kStartTsc; // rdtsc() @ _start() - } + void *p; + uint128_t s; + cthread_spinlock(&rand64_lock); + if (__pid == thepid) { + s = thepool; // normal path + } else { + if (!thepid) { + if (AT_RANDOM && (p = (void *)getauxval(AT_RANDOM))) { + // linux / freebsd kernel supplied entropy + memcpy(&s, p, 16); } else { - // process contention so blend a timestamp - s2 = s1 ^ rdtsc(); + // otherwise initialize w/ cheap timestamp + s = kStartTsc; } - // toss the new pid in there - s2 ^= p1; - // ordering for thepid probably doesn't matter - thepid = p1; + } else { + // blend another timestamp on fork contention + s = thepool ^ rdtsc(); } - // lemur64 pseudorandom number generator - s2 *= 15750249268501108917ull; - // sadly 128-bit values aren't atomic on x86 - _lockcmpxchg16b(&thepool, &s1, s2); - // do it again if there's thread contention - } while (_lockxadd(&thecount, 1) != c1); - // the most important step in the prng - return s2 >> 64; + // blend the pid on startup and fork contention + s ^= __pid; + thepid = __pid; + } + thepool = (s *= 15750249268501108917ull); // lemur64 + cthread_spunlock(&rand64_lock); + return s >> 64; } diff --git a/libc/rand/rdrand.c b/libc/rand/rdrand.c index 2afc1b3d0..b431b4ee5 100644 --- a/libc/rand/rdrand.c +++ b/libc/rand/rdrand.c @@ -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/errno.h" #include "libc/nexgen32e/x86feature.h" #include "libc/rand/rand.h" diff --git a/libc/rand/rdseed.c b/libc/rand/rdseed.c index 9aec3438c..3852f3175 100644 --- a/libc/rand/rdseed.c +++ b/libc/rand/rdseed.c @@ -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/nexgen32e/x86feature.h" #include "libc/rand/rand.h" #include "libc/stdio/stdio.h" diff --git a/libc/runtime/abort-nt.c b/libc/runtime/abort.greg.c similarity index 72% rename from libc/runtime/abort-nt.c rename to libc/runtime/abort.greg.c index 5d80538bd..883542945 100644 --- a/libc/runtime/abort-nt.c +++ b/libc/runtime/abort.greg.c @@ -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 │ @@ -16,22 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/calls/typedef/sigaction_f.h" +#include "libc/calls/calls.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigset.h" +#include "libc/dce.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" -#include "libc/str/str.h" #include "libc/sysv/consts/sig.h" -textwindows wontreturn void sys_abort_nt(void) { - int rva; - siginfo_t info; - bzero(&info, sizeof(info)); - info.si_signo = SIGABRT; - rva = __sighandrvas[SIGABRT]; - if (rva >= kSigactionMinRva) { - if (((sigaction_f)(_base + rva))) { - ((sigaction_f)(_base + rva))(SIGABRT, &info, NULL); - } - } +/** + * Terminates program abnormally. + * + * This function first tries to trigger your SIGABRT handler. If + * there isn't one or execution resumes, then abort() terminates + * the program using an escalating variety methods of increasing + * brutality. + * + * @asyncsignalsafe + * @noreturn + */ +privileged void abort(void) { + sigset_t sm; + sigfillset(&sm); + sigdelset(&sm, SIGABRT); + sigprocmask(SIG_SETMASK, &sm, 0); + raise(SIGABRT); + __restorewintty(); _Exit(128 + SIGABRT); } diff --git a/libc/runtime/arch_prctl.c b/libc/runtime/arch_prctl.c index 547747804..b8123e1e2 100644 --- a/libc/runtime/arch_prctl.c +++ b/libc/runtime/arch_prctl.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/asmflag.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/dce.h" @@ -113,12 +114,14 @@ static int arch_prctl_freebsd(int code, int64_t addr) { static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) { int ax; + bool failed; switch (code) { case ARCH_SET_GS: - asm volatile("syscall" - : "=a"(ax) - : "0"(0x3000003), "D"(addr - 0x8a0 /* wat */) + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax) + : "1"(0x3000003), "D"(addr - 0x8a0 /* wat */) : "rcx", "r11", "memory", "cc"); + if (failed) errno = ax, ax = -1; return ax; case ARCH_GET_FS: case ARCH_SET_FS: @@ -130,21 +133,30 @@ static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) { } static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) { + bool failed; int64_t rax; switch (code) { case ARCH_GET_FS: - asm volatile("syscall" - : "=a"(rax) - : "0"(0x014a /* __get_tcb */) + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(rax) + : "1"(0x014a /* __get_tcb */) : "rcx", "r11", "cc", "memory"); + if (failed) { + errno = rax; + return -1; + } *(int64_t *)addr = rax; return 0; case ARCH_SET_FS: - asm volatile("syscall" - : "=a"(rax) - : "0"(0x0149 /* __set_tcb */), "D"(addr) + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(rax) + : "1"(0x0149 /* __set_tcb */), "D"(addr) : "rcx", "r11", "cc", "memory"); - return 0; + if (failed) { + errno = rax; + rax = -1; + } + return rax; case ARCH_GET_GS: case ARCH_SET_GS: return enosys(); diff --git a/libc/runtime/close_s.c b/libc/runtime/close_s.c index 3246b090f..431f251f9 100644 --- a/libc/runtime/close_s.c +++ b/libc/runtime/close_s.c @@ -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" #include "libc/runtime/runtime.h" /** diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 0d43cd58f..a1ecde86d 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -25,8 +25,10 @@ #include "libc/macros.internal.h" #include "libc/mem/alloca.h" #include "libc/nexgen32e/nt2sysv.h" +#include "libc/nt/console.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" +#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/startf.h" #include "libc/nt/ipc.h" #include "libc/nt/memory.h" @@ -49,6 +51,7 @@ extern unsigned char __data_start[]; /* αpε */ extern unsigned char __data_end[]; /* αpε */ extern unsigned char __bss_start[]; /* αpε */ extern unsigned char __bss_end[]; /* αpε */ +bool32 __onntconsoleevent_nt(uint32_t); static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) { *x = 0; @@ -219,6 +222,9 @@ textwindows void WinMainForked(void) { if (weaken(__wincrash_nt)) { AddVectoredExceptionHandler(1, (void *)weaken(__wincrash_nt)); } + if (weaken(__onntconsoleevent_nt)) { + SetConsoleCtrlHandler(weaken(__onntconsoleevent_nt), 1); + } longjmp(jb, 1); } @@ -257,7 +263,8 @@ textwindows int sys_fork_nt(void) { } #endif if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, - &kNtIsInheritable, NULL, true, 0, NULL, &startinfo, + &kNtIsInheritable, NULL, true, + 0 /* kNtCreateNewProcessGroup */, NULL, &startinfo, &procinfo) != -1) { CloseHandle(reader); CloseHandle(procinfo.hThread); diff --git a/libc/runtime/free_s.c b/libc/runtime/free_s.c index 84622ec8a..b0c8bc545 100644 --- a/libc/runtime/free_s.c +++ b/libc/runtime/free_s.c @@ -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/lockxchg.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 4a5eb0657..4011d5781 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -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/bits/safemacros.internal.h" +#include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" @@ -84,7 +84,7 @@ privileged noinstrument noasan noubsan void ftracer(void) { uint64_t stamp; static bool noreentry; struct StackFrame *frame; - if (!cmpxchg(&noreentry, 0, 1)) return; + if (!_cmpxchg(&noreentry, 0, 1)) return; if (ftrace_enabled && g_symbols) { stamp = rdtsc(); frame = __builtin_frame_address(0); diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 09544b824..d5b9375b3 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -12,7 +12,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern uint32_t __ntconsolemode[2]; +extern uint32_t __ntconsolemode[3]; extern const char v_ntsubsystem[] __attribute__((__weak__)); extern const uintptr_t __fini_array_end[] __attribute__((__weak__)); extern const uintptr_t __fini_array_start[] __attribute__((__weak__)); diff --git a/libc/runtime/pthread.c b/libc/runtime/pthread.c index 62fda4684..89f6b9af8 100644 --- a/libc/runtime/pthread.c +++ b/libc/runtime/pthread.c @@ -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/errno.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/runtime/runtime.h" typedef void *pthread_t; @@ -25,7 +25,7 @@ typedef bool pthread_once_t; typedef int pthread_mutex_t; int pthread_once(pthread_once_t *once, void init(void)) { - if (lockcmpxchg(once, 0, 1)) init(); + if (_lockcmpxchg(once, 0, 1)) init(); return 0; } diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index f0c5aa490..bd7cdaafa 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -80,15 +80,17 @@ struct WinArgs { extern int __pid; extern bool __nomultics; extern uint32_t __winmainpid; -extern const char kConsoleHandles[2]; +extern const char kConsoleHandles[3]; -static const short kConsoleModes[2] = { +static const short kConsoleModes[3] = { kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput | kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags | kNtEnableAutoPosition | kNtEnableInsertMode | kNtEnableVirtualTerminalInput, kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | kNtEnableVirtualTerminalProcessing, + kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | + kNtEnableVirtualTerminalProcessing, }; forceinline void MakeLongDoubleLongAgain(void) { @@ -128,7 +130,7 @@ static noasan textwindows wontreturn noinstrument void WinMainNew( STRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc); rc = SetConsoleOutputCP(kNtCpUtf8); STRACE("SetConsoleOutputCP(kNtCpUtf8) → %hhhd", rc); - for (i = 0; i < 2; ++i) { + for (i = 0; i < 3; ++i) { hand = GetStdHandle(kConsoleHandles[i]); rc = GetConsoleMode(hand, __ntconsolemode + i); STRACE("GetConsoleMode(%p, [%#x]) → %hhhd", hand, __ntconsolemode[i], rc); diff --git a/libc/stdio/fclose_s.c b/libc/stdio/fclose_s.c index 7fa0b4165..aeb86aa01 100644 --- a/libc/stdio/fclose_s.c +++ b/libc/stdio/fclose_s.c @@ -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/lockxchg.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/sysv/calls/setpgid.s b/libc/sysv/calls/setpgid.s deleted file mode 100644 index 065e3d7ab..000000000 --- a/libc/sysv/calls/setpgid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall setpgid,0x052052052205206d,globl diff --git a/libc/sysv/calls/sys_setpgid.s b/libc/sysv/calls/sys_setpgid.s new file mode 100644 index 000000000..81db109ca --- /dev/null +++ b/libc/sysv/calls/sys_setpgid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_setpgid,0x052052052205206d,globl,hidden diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 9a890b28c..20ae378be 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -149,7 +149,7 @@ scall getpgrp 0x051051051205106f globl scall sys_setsid 0x0930930932093070 globl hidden scall sys_getsid 0x11e0ff136213607c globl hidden scall sys_getpgid 0x0cf0cf0cf2097079 globl hidden -scall setpgid 0x052052052205206d globl +scall sys_setpgid 0x052052052205206d globl hidden scall geteuid 0xfff019019201906b globl scall getegid 0xfff02b02b202b06c globl scall getgroups 0x04f04f04f204f073 globl diff --git a/libc/thread/freebsd.internal.h b/libc/thread/freebsd.internal.h new file mode 100644 index 000000000..3618f2c3d --- /dev/null +++ b/libc/thread/freebsd.internal.h @@ -0,0 +1,112 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ +#include "libc/bits/asmflag.h" +#include "libc/calls/struct/timespec.h" +#include "libc/errno.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/** + * @fileoverview FreeBSD Threading + * + * @note even though FreeBSD uses a 64-bit type for thread IDs the + * maximum legal range is PID_MAX + 2 (100001) through INT_MAX + */ + +struct rtprio { + uint16_t type; /* scheduling class */ + uint16_t prio; +}; + +struct thr_param { + void (*start_func)(void *); + void *arg; + char *stack_base; + uint64_t stack_size; + char *tls_base; + uint64_t tls_size; + int64_t *child_tid; + int64_t *parent_tid; + int32_t flags; + struct rtprio *rtp; +}; + +static inline wontreturn void thr_exit(int64_t *opt_out_state) { + long ax, di; + asm volatile("syscall" + : "=a"(ax) + : "0"(431), "D"(opt_out_state) + : "memory", "cc"); + unreachable; +} + +static inline int thr_new(struct thr_param *param, int param_size) { + bool failed; + long ax, di, si; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si) + : "1"(455), "2"(param), "3"(param_size) + : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +static inline int thr_kill(int64_t id, int sig) { + bool failed; + long ax, di, si; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si) + : "1"(433), "2"(id), "3"(sig) + : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +static inline int thr_suspend(const struct timespec *opt_timeout) { + bool failed; + long ax, di; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di) + : "1"(442), "2"(opt_timeout) + : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +static inline int thr_wake(int64_t id) { + bool failed; + long ax, di; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di) + : "1"(443), "2"(id) + : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +static inline int thr_set_name(int64_t id, const char *name) { + bool failed; + long ax, di, si; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si) + : "1"(464), "2"(id), "3"(name) + : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +static inline int thr_kill2(int pid, int64_t id, int sig) { + bool failed; + long ax, di, si, dx; + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si), + "=d"(dx) + : "1"(481), "2"(pid), "3"(id), "4"(sig) + : "rcx", "r8", "r9", "r10", "r11", "memory"); + if (failed) ax = -ax; + return ax; +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */ diff --git a/libc/thread/sem.c b/libc/thread/sem.c index 5d005cdb8..b4c98b2ad 100644 --- a/libc/thread/sem.c +++ b/libc/thread/sem.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/atomic.h" +#include "libc/intrin/atomic_load.h" #include "libc/thread/sem.h" #include "libc/thread/wait.h" #include "libc/thread/yield.h" @@ -37,6 +38,7 @@ int cthread_sem_init(cthread_sem_t* sem, int count) { sem->linux.count = count; return 0; } + int cthread_sem_destroy(cthread_sem_t* sem) { (void)sem; return 0; @@ -44,7 +46,7 @@ int cthread_sem_destroy(cthread_sem_t* sem) { int cthread_sem_signal(cthread_sem_t* sem) { uint64_t count; - asm volatile("lock xadd\t%1, %0" + asm volatile("lock xadd\t%1,%0" : "+m"(sem->linux.count), "=r"(count) : "1"(1) : "cc"); @@ -62,7 +64,7 @@ int cthread_sem_wait_futex(cthread_sem_t* sem, const struct timespec* timeout) { uint64_t count; // record current thread as waiter - asm volatile("lock xadd\t%1, %0" + asm volatile("lock xadd\t%1,%0" : "+m"(sem->linux.count), "=r"(count) : "1"((uint64_t)1 << CTHREAD_THREAD_VAL_BITS) : "cc"); @@ -77,7 +79,7 @@ int cthread_sem_wait_futex(cthread_sem_t* sem, const struct timespec* timeout) { return 0; } } - + // WARNING: an offset of 4 bytes would be required on little-endian archs void* wait_address = &sem->linux.count; cthread_memory_wait32(wait_address, count, timeout); @@ -91,16 +93,17 @@ int cthread_sem_wait_spin(cthread_sem_t* sem, uint64_t count, int spin, const struct timespec* timeout) { // spin on pause for (int attempt = 0; attempt < spin; ++attempt) { - //if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break; + // if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break; while ((uint32_t)count > 0) { - // spin is useful if multiple waiters can acquire the semaphore at the same time + // spin is useful if multiple waiters can acquire the semaphore at the + // same time if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { return 0; } } pause(attempt); } - + return cthread_sem_wait_futex(sem, timeout); } @@ -110,11 +113,12 @@ int cthread_sem_wait(cthread_sem_t* sem, int spin, // uncontended while ((uint32_t)count > 0) { - // spin is useful if multiple waiters can acquire the semaphore at the same time + // spin is useful if multiple waiters can acquire the semaphore at the same + // time if (atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) { return 0; } } - + return cthread_sem_wait_spin(sem, count, spin, timeout); } diff --git a/libc/thread/xnu.internal.h b/libc/thread/xnu.internal.h new file mode 100644 index 000000000..04b530704 --- /dev/null +++ b/libc/thread/xnu.internal.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/** + * XNU thread system calls. + * @see darwin-libpthread/kern/kern_support.c + */ + +void *bsdthread_create(void *func, void *func_arg, void *stack, void *pthread, + uint32_t flags); +int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port, + uint32_t sem); +int bsdthread_register(void *threadstart, void *wqthread, uint32_t flags, + void *stack_addr_hint, void *targetconc_ptr, + uint32_t dispatchqueue_offset, uint32_t tsd_offset); +int bsdthread_ctl(void *cmd, void *arg1, void *arg2, void *arg3); +uint64_t thread_selfid(void); +uint64_t thread_selfusage(void); +int thread_selfcounts(int type, void *buf, uint64_t nbytes); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_XNU_INTERNAL_H_ */ diff --git a/tool/plinko/lib/time.h b/libc/time/clockstonanos.internal.h similarity index 68% rename from tool/plinko/lib/time.h rename to libc/time/clockstonanos.internal.h index 4177de518..c44f78cec 100644 --- a/tool/plinko/lib/time.h +++ b/libc/time/clockstonanos.internal.h @@ -1,5 +1,5 @@ -#ifndef COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ -#define COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ +#ifndef COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -12,4 +12,4 @@ static inline uint64_t ClocksToNanos(uint64_t x, uint64_t y) { COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_TOOL_PLINKO_LIB_TIME_H_ */ +#endif /* COSMOPOLITAN_LIBC_TIME_CLOCKSTONANOS_INTERNAL_H_ */ diff --git a/libc/x/xload.c b/libc/x/xload.c index 6219e2a8b..0dfc6cc2f 100644 --- a/libc/x/xload.c +++ b/libc/x/xload.c @@ -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/lockcmpxchg.h" #include "libc/log/check.h" #include "libc/runtime/runtime.h" #include "libc/x/x.h" @@ -58,7 +58,7 @@ void *xload(bool *o, void **t, const void *p, size_t n, size_t m) { zs.next_out = (void *)q; inflateInit2(&zs, -MAX_WBITS); inflate(&zs, Z_NO_FLUSH); - if (lockcmpxchg(t, 0, q)) { + if (_lockcmpxchg(t, 0, q)) { __cxa_atexit(free, q, 0); } else { free(q); diff --git a/libc/x/xloadzd.c b/libc/x/xloadzd.c index 5a04a0494..2444b3abf 100644 --- a/libc/x/xloadzd.c +++ b/libc/x/xloadzd.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/fmt/leb128.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/runtime.h" #include "libc/x/x.h" @@ -69,7 +69,7 @@ void *xloadzd(bool *o, void **t, const void *p, size_t n, size_t m, size_t c, } free(q); assert(crc32_z(0, r, c * z) == s); - if (lockcmpxchg(t, 0, r)) { + if (_lockcmpxchg(t, 0, r)) { __cxa_atexit(free, r, 0); } else { free(q); diff --git a/net/https/sslcache.c b/net/https/sslcache.c index 0d48c8ae3..664f5f0ca 100644 --- a/net/https/sslcache.c +++ b/net/https/sslcache.c @@ -21,6 +21,7 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -129,7 +130,7 @@ int UncacheSslSession(void *data, mbedtls_ssl_session *session) { ts = time(0); if (!(e->time <= ts && ts <= e->time + cache->lifetime)) { DEBUGF("%u sslcache expired", i); - lockcmpxchg(&e->tick, tick, 0); + _lockcmpxchg(&e->tick, tick, 0); return 1; } cert = 0; @@ -199,7 +200,7 @@ int CacheSslSession(void *data, const mbedtls_ssl_session *session) { e->time = time(0); tick = rdtsc(); asm volatile("" ::: "memory"); - if (tick && lockcmpxchg(&e->pid, pid, 0)) { + if (tick && _lockcmpxchg(&e->pid, pid, 0)) { DEBUGF("%u saved %s%s %`#.*s", i, mbedtls_ssl_get_ciphersuite_name(session->ciphersuite), session->compression ? " DEFLATE" : "", session->id_len, diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 1d9d7c8e2..eb4e6ad0b 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -23,6 +23,7 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" @@ -39,37 +40,10 @@ void OnSigInt(int sig) { void SetUp(void) { gotsigint = false; - /* TODO(jart): Windows needs huge signal overhaul */ - if (IsWindows()) exit(0); } -TEST(sigaction, test) { - int pid, status; - sigset_t block, ignore, oldmask; - struct sigaction saint = {.sa_handler = OnSigInt}; - sigemptyset(&block); - sigaddset(&block, SIGINT); - EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &block, &oldmask)); - sigfillset(&ignore); - sigdelset(&ignore, SIGINT); - EXPECT_NE(-1, sigaction(SIGINT, &saint, &oldsa)); - ASSERT_NE(-1, (pid = fork())); - if (!pid) { - EXPECT_NE(-1, kill(getppid(), SIGINT)); - EXPECT_EQ(-1, sigsuspend(&ignore)); - EXPECT_EQ(EINTR, errno); - EXPECT_TRUE(gotsigint); - _exit(0); - } - EXPECT_EQ(-1, sigsuspend(&ignore)); - EXPECT_NE(-1, kill(pid, SIGINT)); - EXPECT_NE(-1, waitpid(pid, &status, 0)); - EXPECT_EQ(1, WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); - EXPECT_EQ(0, WTERMSIG(status)); - EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &oldmask, NULL)); - EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL)); -} +//////////////////////////////////////////////////////////////////////////////// +// test raise() TEST(sigaction, raise) { struct sigaction saint = {.sa_handler = OnSigInt}; @@ -80,6 +54,58 @@ TEST(sigaction, raise) { EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL)); } +//////////////////////////////////////////////////////////////////////////////// +// test kill() + +TEST(sigaction, testPingPongParentChildWithSigint) { + int pid, status; + sigset_t blockint, oldmask; + struct sigaction oldint; + struct sigaction ignoreint = {.sa_handler = SIG_IGN}; + struct sigaction catchint = {.sa_handler = OnSigInt}; + if (IsWindows()) { + // this works if it's run by itself on the command prompt. but it + // doesn't currently work if it's launched as a subprocess of some + // kind of runner. todo(fixme!) + return; + } + EXPECT_NE(-1, sigemptyset(&blockint)); + EXPECT_NE(-1, sigaddset(&blockint, SIGINT)); + EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &blockint, &oldmask)); + EXPECT_NE(-1, sigaction(SIGINT, &catchint, &oldint)); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + // ping + EXPECT_NE(-1, kill(getppid(), SIGINT)); + EXPECT_FALSE(gotsigint); + // pong + EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); + EXPECT_EQ(-1, sigsuspend(0)); + EXPECT_EQ(EINTR, errno); + EXPECT_TRUE(gotsigint); + _exit(0); + } + // pong + EXPECT_FALSE(gotsigint); + EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); + EXPECT_EQ(-1, sigsuspend(0)); + EXPECT_TRUE(gotsigint); + // ping + EXPECT_NE(-1, sigaction(SIGINT, &ignoreint, 0)); + EXPECT_NE(-1, kill(pid, SIGINT)); + // cleanup + EXPECT_NE(-1, wait4(pid, &status, 0, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); + EXPECT_EQ(0, WTERMSIG(status)); + EXPECT_NE(-1, sigaction(SIGINT, &oldint, 0)); + EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &oldmask, 0)); +} + +//////////////////////////////////////////////////////////////////////////////// +// test int3 crash +// we expect this to be recoverable by default + volatile int trapeax; void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) { @@ -94,6 +120,10 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) { EXPECT_NE(-1, sigaction(SIGTRAP, &oldsa, NULL)); } +//////////////////////////////////////////////////////////////////////////////// +// test fpu crash (unrecoverable) +// test signal handler can modify cpu registers (now it's recoverable!) + void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { struct XedDecodedInst xedd; xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); diff --git a/test/libc/calls/signal_test.c b/test/libc/calls/signal_test.c index 3c1330b09..7dc90e833 100644 --- a/test/libc/calls/signal_test.c +++ b/test/libc/calls/signal_test.c @@ -24,13 +24,12 @@ #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" -testonly void OnCtrlC(int sig) { +testonly void OnUsr1(int sig) { _exit(0); } TEST(signal, test) { - if (IsWindows()) return; /* omg */ - ASSERT_NE(SIG_ERR, signal(SIGINT, OnCtrlC)); - ASSERT_NE(-1, raise(SIGINT)); + ASSERT_NE(SIG_ERR, signal(SIGUSR1, OnUsr1)); + ASSERT_NE(-1, raise(SIGUSR1)); __die(); } diff --git a/test/libc/calls/sigprocmask_test.c b/test/libc/calls/sigprocmask_test.c index 1b806d0d2..5ccc94071 100644 --- a/test/libc/calls/sigprocmask_test.c +++ b/test/libc/calls/sigprocmask_test.c @@ -23,11 +23,12 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" +#include "libc/intrin/kprintf.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" -int n; +volatile int n; void OnSig(int sig, siginfo_t *si, ucontext_t *ctx) { ++n; diff --git a/test/libc/calls/sigsuspend_test.c b/test/libc/calls/sigsuspend_test.c index 0277e7cfd..7cd46385e 100644 --- a/test/libc/calls/sigsuspend_test.c +++ b/test/libc/calls/sigsuspend_test.c @@ -46,10 +46,6 @@ void OnSigQueuing(int sig, siginfo_t *si, ucontext_t *ctx) { } TEST(sigsuspend, testSignalQueuingSelf) { - if (IsWindows()) { - // xxx: probably need a signal server to do this kind of signalling - return; - } sigset_t neu, old, bits; struct sigaction oldusr1, oldusr2; struct sigaction sa = {.sa_sigaction = OnSigQueuing, .sa_flags = SA_SIGINFO}; diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index ae958bea5..542e06775 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -231,7 +231,6 @@ TEST(ksnprintf, fuzzTheUnbreakable) { } TEST(kprintf, testFailure_wontClobberErrnoAndBypassesSystemCallSupport) { - if (IsWindows()) return; // TODO(jart): fixme int n; ASSERT_EQ(0, errno); EXPECT_SYS(0, 3, dup(2)); @@ -344,30 +343,27 @@ TEST(ksnprintf, badUtf16) { BENCH(printf, bench) { char b[128]; int snprintf_(char *, size_t, const char *, ...) asm("snprintf"); - EZBENCH2("ksnprintf fmt", donothing, - ksnprintf(b, 128, - "hello world\nhello world\nhello world\nhello world\n")); - EZBENCH2("snprintf fmt", donothing, - snprintf_(b, 128, - "hello world\nhello world\nhello world\nhello world\n")); - EZBENCH2("ksnprintf str", donothing, - ksnprintf(b, 128, "%s\n", "hello world")); + EZBENCH2("ksnprintf fmt", donothing, ksnprintf(b, 128, ".")); + EZBENCH2("kusnprintf fmt", donothing, kusnprintf(b, 128, ".")); + EZBENCH2("snprintf fmt", donothing, snprintf_(b, 128, ".")); + EZBENCH2("kusnprintf str", donothing, + kusnprintf(b, 128, "%s\n", "hello world")); EZBENCH2("snprintf str", donothing, snprintf_(b, 128, "%s\n", "hello world")); - EZBENCH2("ksnprintf utf8", donothing, - ksnprintf(b, 128, "%s\n", "天地玄黄宇宙洪荒天地玄黄宇宙洪荒")); + EZBENCH2("kusnprintf utf8", donothing, + kusnprintf(b, 128, "%s\n", "天地玄黄宇宙洪荒天地玄黄宇宙洪荒")); EZBENCH2("snprintf utf8", donothing, snprintf_(b, 128, "%s\n", "天地玄黄宇宙洪荒天地玄黄宇宙洪荒")); - EZBENCH2("ksnprintf chinese", donothing, - ksnprintf(b, 128, "%hs\n", u"天地玄黄宇宙洪荒")); + EZBENCH2("kusnprintf chinese", donothing, + kusnprintf(b, 128, "%hs\n", u"天地玄黄宇宙洪荒")); EZBENCH2("snprintf chinese", donothing, snprintf_(b, 128, "%hs\n", u"天地玄黄宇宙洪荒")); - EZBENCH2("ksnprintf astral", donothing, - ksnprintf(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷")); + EZBENCH2("kusnprintf astral", donothing, + kusnprintf(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷")); EZBENCH2("snprintf astral", donothing, snprintf_(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷")); - EZBENCH2("ksnprintf long", donothing, ksnprintf(b, 128, "%ld", LONG_MAX)); + EZBENCH2("kusnprintf long", donothing, kusnprintf(b, 128, "%ld", LONG_MAX)); EZBENCH2("snprintf long", donothing, snprintf_(b, 128, "%ld", LONG_MAX)); - EZBENCH2("ksnprintf thou", donothing, ksnprintf(b, 128, "%'ld", LONG_MAX)); + EZBENCH2("kusnprintf thou", donothing, kusnprintf(b, 128, "%'ld", LONG_MAX)); EZBENCH2("snprintf thou", donothing, snprintf_(b, 128, "%'ld", LONG_MAX)); } diff --git a/test/libc/rand/rand64_test.c b/test/libc/rand/rand64_test.c index a2bd4d5fa..38a0a1fcf 100644 --- a/test/libc/rand/rand64_test.c +++ b/test/libc/rand/rand64_test.c @@ -30,8 +30,8 @@ #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" -#define THREADS 1 -#define ENTRIES 10 +#define THREADS 4 +#define ENTRIES 256 volatile bool ready; volatile uint64_t A[THREADS * ENTRIES]; @@ -73,7 +73,6 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART}; EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa)); bzero(A, sizeof(A)); - rand64(); // for effect sigemptyset(&ss); sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); diff --git a/third_party/make/main.c b/third_party/make/main.c index c845f8562..2fa0845b2 100644 --- a/third_party/make/main.c +++ b/third_party/make/main.c @@ -781,7 +781,7 @@ handle_runtime_exceptions (struct _EXCEPTION_POINTERS *exinfo) /* turn this on if we want to put stuff in the event log too */ #ifdef USE_EVENT_LOG - hEventSource = RegisterEventSource (NULL, "GNU Make"); + hEventSource = RegisterEventSource (NULL, u"GNU Make"); lpszStrings[0] = errmsg; if (hEventSource != NULL) diff --git a/third_party/python/Modules/expat/xmlparse.c b/third_party/python/Modules/expat/xmlparse.c index ff65d07c7..831d46e99 100644 --- a/third_party/python/Modules/expat/xmlparse.c +++ b/third_party/python/Modules/expat/xmlparse.c @@ -1,5 +1,4 @@ #include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timeval.h" #include "libc/errno.h" @@ -596,29 +595,11 @@ static unsigned long get_hash_secret_salt(XML_Parser parser) { return parser->m_hash_secret_salt; } -static uint64_t getsome(void) { - int i; - char cf; - uint64_t x; - if (X86_HAVE(RDRND)) { - for (i = 0; i < 10; ++i) { - asm volatile(CFLAG_ASM("rdrand\t%1") - : CFLAG_CONSTRAINT(cf), "=r"(x) - : /* no inputs */ - : "cc"); - if (cf) return x; - asm volatile("pause"); - } - } - if (getrandom(&x, 8, 0) != 8) abort(); - return x; -} - static XML_Bool /* only valid for root parser */ startParsing(XML_Parser parser) { /* hash functions must be initialized before setContext() is called */ if (!parser->m_hash_secret_salt) { - parser->m_hash_secret_salt = getsome(); + parser->m_hash_secret_salt = rdrand(); } if (parser->m_ns) { /* implicit context only set for root parser, since child diff --git a/third_party/python/Modules/getpath.c b/third_party/python/Modules/getpath.c index d23fed498..d45728be0 100644 --- a/third_party/python/Modules/getpath.c +++ b/third_party/python/Modules/getpath.c @@ -4,10 +4,10 @@ │ Python 3 │ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" +#include "libc/intrin/cmpxchg.h" #include "libc/log/log.h" #include "libc/mem/alloca.h" #include "libc/mem/mem.h" @@ -662,7 +662,7 @@ wchar_t * Py_GetProgramFullPath(void) { static bool once; - if (cmpxchg(&once, false, true)) { + if (_cmpxchg(&once, false, true)) { progpath = utf8toutf32(program_executable_name, -1, 0); __cxa_atexit(free, progpath, 0); } diff --git a/third_party/python/Python/getcopyright.c b/third_party/python/Python/getcopyright.c index e19d101d3..360273ce5 100644 --- a/third_party/python/Python/getcopyright.c +++ b/third_party/python/Python/getcopyright.c @@ -4,8 +4,8 @@ │ Python 3 │ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" #include "libc/bits/weaken.h" +#include "libc/intrin/cmpxchg.h" #include "libc/stdio/append.internal.h" #include "libc/str/str.h" #include "third_party/python/Include/pylifecycle.h" @@ -30,7 +30,7 @@ Py_GetCopyright(void) const char *p; static bool once; static char *res; - if (cmpxchg(&once, 0, 1)) { + if (_cmpxchg(&once, 0, 1)) { appends(&res, ""); for (p = *weaken(kLegalNotices); *p; p += strlen(p) + 1) { appends(&res, p); diff --git a/third_party/python/Python/random.c b/third_party/python/Python/random.c index 16488afd3..ea905e280 100644 --- a/third_party/python/Python/random.c +++ b/third_party/python/Python/random.c @@ -5,7 +5,6 @@ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -389,35 +388,10 @@ _PyOS_URandomNonblock(void *buffer, Py_ssize_t size) return pyurandom(buffer, size, 0, 1); } -static uint64_t -getsome(void) -{ - int i; - char cf; - uint64_t x; - for (i = 0; i < 10; ++i) { - asm volatile(CFLAG_ASM("rdrand\t%1") - : CFLAG_CONSTRAINT(cf), "=r"(x) - : /* no inputs */ - : "cc"); - if (cf) return x; - asm volatile("pause"); - } - while (getrandom(&x, sizeof(x), GRND_NONBLOCK) != sizeof(x)) { - if (errno != EINTR) { - x ^= rdtsc(); - x += getpid(); - break; - } - } - return x; -} - void _PyRandom_Init(void) { char *env; - uint64_t x; const unsigned char *auxrng; unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc; Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); @@ -456,7 +430,8 @@ _PyRandom_Init(void) } } else { - int res; + uint64_t x; + int res, i, j; /* _PyRandom_Init() is called very early in the Python initialization and so exceptions cannot be used (use raise=0). _PyRandom_Init() must not block Python initialization: call @@ -465,15 +440,14 @@ _PyRandom_Init(void) /* * [jart] modified to be more efficient */ - x = getsome(); - memcpy(secret, &x, 8); - if ((auxrng = (const unsigned char *)getauxval(AT_RANDOM))) { - memcpy(secret + 8, auxrng, 16); - } else { - x = getsome(); - memcpy(secret + 8, &x, 8); - x = getsome(); - memcpy(secret + 16, &x, 8); + for (i = 0; i < secret_size;) { + x = rdrand(); // will failover to getrandom() etc. + for (j = 0; j < 8; ++j) { + if (i < secret_size) { + secret[i++] = x; + x >>= 8; + } + } } #else res = pyurandom(secret, secret_size, 0, 0); diff --git a/tool/plinko/lib/plinko.c b/tool/plinko/lib/plinko.c index 2b52131a0..b7f5b37d4 100644 --- a/tool/plinko/lib/plinko.c +++ b/tool/plinko/lib/plinko.c @@ -34,6 +34,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sig.h" +#include "libc/time/clockstonanos.internal.h" #include "third_party/getopt/getopt.h" #include "tool/build/lib/case.h" #include "tool/plinko/lib/char.h" @@ -45,7 +46,6 @@ #include "tool/plinko/lib/print.h" #include "tool/plinko/lib/printf.h" #include "tool/plinko/lib/stack.h" -#include "tool/plinko/lib/time.h" #include "tool/plinko/lib/trace.h" #include "tool/plinko/lib/tree.h" diff --git a/tool/plinko/lib/printf.c b/tool/plinko/lib/printf.c index 2a13521b0..839ef7216 100644 --- a/tool/plinko/lib/printf.c +++ b/tool/plinko/lib/printf.c @@ -20,11 +20,11 @@ #include "libc/nexgen32e/rdtsc.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/time/clockstonanos.internal.h" #include "tool/plinko/lib/char.h" #include "tool/plinko/lib/plinko.h" #include "tool/plinko/lib/print.h" #include "tool/plinko/lib/printf.h" -#include "tool/plinko/lib/time.h" static inline long GetVarInt(va_list va, signed char t) { if (t <= 0) return va_arg(va, int);