mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Make improvements
- Every unit test now passes on Apple Silicon. The final piece of this puzzle was porting our POSIX threads cancelation support, since that works differently on ARM64 XNU vs. AMD64. Our semaphore support on Apple Silicon is also superior now compared to AMD64, thanks to the grand central dispatch library which lets *NSYNC locks go faster. - The Cosmopolitan runtime is now more stable, particularly on Windows. To do this, thread local storage is mandatory at all runtime levels, and the innermost packages of the C library is no longer being built using ASAN. TLS is being bootstrapped with a 128-byte TIB during the process startup phase, and then later on the runtime re-allocates it either statically or dynamically to support code using _Thread_local. fork() and execve() now do a better job cooperating with threads. We can now check how much stack memory is left in the process or thread when functions like kprintf() / execve() etc. call alloca(), so that ENOMEM can be raised, reduce a buffer size, or just print a warning. - POSIX signal emulation is now implemented the same way kernels do it with pthread_kill() and raise(). Any thread can interrupt any other thread, regardless of what it's doing. If it's blocked on read/write then the killer thread will cancel its i/o operation so that EINTR can be returned in the mark thread immediately. If it's doing a tight CPU bound operation, then that's also interrupted by the signal delivery. Signal delivery works now by suspending a thread and pushing context data structures onto its stack, and redirecting its execution to a trampoline function, which calls SetThreadContext(GetCurrentThread()) when it's done. - We're now doing a better job managing locks and handles. On NetBSD we now close semaphore file descriptors in forked children. Semaphores on Windows can now be canceled immediately, which means mutexes/condition variables will now go faster. Apple Silicon semaphores can be canceled too. We're now using Apple's pthread_yield() funciton. Apple _nocancel syscalls are now used on XNU when appropriate to ensure pthread_cancel requests aren't lost. The MbedTLS library has been updated to support POSIX thread cancelations. See tool/build/runitd.c for an example of how it can be used for production multi-threaded tls servers. Handles on Windows now leak less often across processes. All i/o operations on Windows are now overlapped, which means file pointers can no longer be inherited across dup() and fork() for the time being. - We now spawn a thread on Windows to deliver SIGCHLD and wakeup wait4() which means, for example, that posix_spawn() now goes 3x faster. POSIX spawn is also now more correct. Like Musl, it's now able to report the failure code of execve() via a pipe although our approach favors using shared memory to do that on systems that have a true vfork() function. - We now spawn a thread to deliver SIGALRM to threads when setitimer() is used. This enables the most precise wakeups the OS makes possible. - The Cosmopolitan runtime now uses less memory. On NetBSD for example, it turned out the kernel would actually commit the PT_GNU_STACK size which caused RSS to be 6mb for every process. Now it's down to ~4kb. On Apple Silicon, we reduce the mandatory upstream thread size to the smallest possible size to reduce the memory overhead of Cosmo threads. The examples directory has a program called greenbean which can spawn a web server on Linux with 10,000 worker threads and have the memory usage of the process be ~77mb. The 1024 byte overhead of POSIX-style thread-local storage is now optional; it won't be allocated until the pthread_setspecific/getspecific functions are called. On Windows, the threads that get spawned which are internal to the libc implementation use reserve rather than commit memory, which shaves a few hundred kb. - sigaltstack() is now supported on Windows, however it's currently not able to be used to handle stack overflows, since crash signals are still generated by WIN32. However the crash handler will still switch to the alt stack, which is helpful in environments with tiny threads. - Test binaries are now smaller. Many of the mandatory dependencies of the test runner have been removed. This ensures many programs can do a better job only linking the the thing they're testing. This caused the test binaries for LIBC_FMT for example, to decrease from 200kb to 50kb - long double is no longer used in the implementation details of libc, except in the APIs that define it. The old code that used long double for time (instead of struct timespec) has now been thoroughly removed. - ShowCrashReports() is now much tinier in MODE=tiny. Instead of doing backtraces itself, it'll just print a command you can run on the shell using our new `cosmoaddr2line` program to view the backtrace. - Crash report signal handling now works in a much better way. Instead of terminating the process, it now relies on SA_RESETHAND so that the default SIG_IGN behavior can terminate the process if necessary. - Our pledge() functionality has now been fully ported to AARCH64 Linux.
This commit is contained in:
parent
c4eb838516
commit
ec480f5aa0
638 changed files with 7925 additions and 8282 deletions
8
Makefile
8
Makefile
|
@ -146,8 +146,9 @@ include third_party/dlmalloc/dlmalloc.mk #─┘
|
|||
include libc/mem/mem.mk #─┐
|
||||
include third_party/gdtoa/gdtoa.mk # ├──DYNAMIC RUNTIME
|
||||
include third_party/nsync/mem/mem.mk # │ You can now use stdio
|
||||
include libc/thread/thread.mk # │ You can finally call malloc()
|
||||
include tool/hello/hello.mk # │
|
||||
include libc/proc/proc.mk # │ You can now use threads
|
||||
include libc/thread/thread.mk # │ You can now use processes
|
||||
include tool/hello/hello.mk # │ You can finally call malloc()
|
||||
include third_party/zlib/zlib.mk # │
|
||||
include libc/stdio/stdio.mk # │
|
||||
include libc/time/time.mk # │
|
||||
|
@ -243,6 +244,7 @@ include test/libc/xed/test.mk
|
|||
include test/libc/fmt/test.mk
|
||||
include test/libc/dns/test.mk
|
||||
include test/libc/time/test.mk
|
||||
include test/libc/proc/test.mk
|
||||
include test/libc/stdio/test.mk
|
||||
include test/libc/release/test.mk
|
||||
include test/libc/test.mk
|
||||
|
@ -331,6 +333,7 @@ COSMOPOLITAN_OBJECTS = \
|
|||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_REGEX \
|
||||
LIBC_THREAD \
|
||||
LIBC_PROC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
|
@ -376,6 +379,7 @@ COSMOPOLITAN_HEADERS = \
|
|||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_PROC \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_XED \
|
||||
LIBC_STR \
|
||||
|
|
20
ape/ape-m1.c
20
ape/ape-m1.c
|
@ -33,7 +33,7 @@
|
|||
|
||||
#define pagesz 16384
|
||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||
#define SYSLIB_VERSION 2
|
||||
#define SYSLIB_VERSION 4
|
||||
|
||||
struct Syslib {
|
||||
int magic;
|
||||
|
@ -59,6 +59,15 @@ struct Syslib {
|
|||
/* v2 (2023-09-10) */
|
||||
pthread_t (*pthread_self)(void);
|
||||
void (*dispatch_release)(dispatch_semaphore_t);
|
||||
int (*raise)(int);
|
||||
int (*pthread_join)(pthread_t, void **);
|
||||
void (*pthread_yield_np)(void);
|
||||
int pthread_stack_min;
|
||||
int sizeof_pthread_attr_t;
|
||||
int (*pthread_attr_init)(pthread_attr_t *);
|
||||
int (*pthread_attr_destroy)(pthread_attr_t *);
|
||||
int (*pthread_attr_setstacksize)(pthread_attr_t *, size_t);
|
||||
int (*pthread_attr_setguardsize)(pthread_attr_t *, size_t);
|
||||
};
|
||||
|
||||
#define ELFCLASS32 1
|
||||
|
@ -834,6 +843,15 @@ int main(int argc, char **argv, char **envp) {
|
|||
M->lib.dispatch_walltime = dispatch_walltime;
|
||||
M->lib.pthread_self = pthread_self;
|
||||
M->lib.dispatch_release = dispatch_release;
|
||||
M->lib.raise = raise;
|
||||
M->lib.pthread_join = pthread_join;
|
||||
M->lib.pthread_yield_np = pthread_yield_np;
|
||||
M->lib.pthread_stack_min = PTHREAD_STACK_MIN;
|
||||
M->lib.sizeof_pthread_attr_t = sizeof(pthread_attr_t);
|
||||
M->lib.pthread_attr_init = pthread_attr_init;
|
||||
M->lib.pthread_attr_destroy = pthread_attr_destroy;
|
||||
M->lib.pthread_attr_setstacksize = pthread_attr_setstacksize;
|
||||
M->lib.pthread_attr_setguardsize = pthread_attr_setguardsize;
|
||||
|
||||
/* getenv("_") is close enough to at_execfn */
|
||||
execfn = argc > 0 ? argv[0] : 0;
|
||||
|
|
37
ape/ape.S
37
ape/ape.S
|
@ -775,30 +775,19 @@ ape_loader_end:
|
|||
.stub ape_ram_memsz,quad
|
||||
.stub ape_ram_align,quad
|
||||
|
||||
// APE Stack Configuration
|
||||
//
|
||||
// We actually respect this when allocating a deterministically
|
||||
// addressed stack on Windows and other operating systems. However
|
||||
// there's a few caveats:
|
||||
//
|
||||
// - If ape_stack_pf has PF_X then OpenBSD probably won't work
|
||||
// - We don't bother creating a deterministic stack in MODE=tiny
|
||||
//
|
||||
// With that said, here's some examples of configuration:
|
||||
//
|
||||
// STATIC_SYMBOL("ape_stack_pf", "7"); // RWX
|
||||
// STATIC_STACK_ADDR(0x6fffffff0000);
|
||||
// STATIC_STACK_SIZE(65536);
|
||||
//
|
||||
// @see ape.lds for defaults
|
||||
// These values are left empty because some UNIX OSes give p_filesz
|
||||
// priority over `ulimit -s` a.k.a. RLIMIT_STACK which is preferred
|
||||
// because we use an 8mb stack by default so that decadent software
|
||||
// doesn't unexpectedly crash, but putting that here meant NetBSD's
|
||||
// rusage accounting (which is the best) always reported 6mb of RSS
|
||||
.long PT_GNU_STACK
|
||||
.stub ape_stack_pf,long // override w/ PF_X for execstack
|
||||
.stub ape_stack_offset,quad // ignored
|
||||
.stub ape_stack_vaddr,quad // is mmap()'d with MAP_FIXED
|
||||
.stub ape_stack_paddr,quad // ignored
|
||||
.stub ape_stack_filesz,quad // ignored
|
||||
.stub ape_stack_memsz,quad // ignored?
|
||||
.stub ape_stack_align,quad // must be 16+
|
||||
.stub ape_stack_pf,long
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.stub ape_stack_align,quad
|
||||
|
||||
#if SupportsOpenbsd() || SupportsNetbsd()
|
||||
.long PT_NOTE
|
||||
|
@ -1086,7 +1075,7 @@ ape_pe: .ascin "PE",4
|
|||
.short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console
|
||||
.short v_ntdllchar // DllCharacteristics
|
||||
.quad 0x10000 // StackReserve
|
||||
.quad 0x10000 // StackCommit
|
||||
.quad 0x1000 // StackCommit
|
||||
.quad 0 // HeapReserve
|
||||
.quad 0 // HeapCommit
|
||||
.long 0 // LoaderFlags
|
||||
|
|
15
bin/cosmoaddr2line
Executable file
15
bin/cosmoaddr2line
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
set -- -apifCe "$@"
|
||||
|
||||
if [ -n "$ADDR2LINE" ]; then
|
||||
exec "$ADDR2LINE" "$@"
|
||||
fi
|
||||
|
||||
COSMO=${COSMO:-/opt/cosmo}
|
||||
for ARCH in x86_64 aarch64; do
|
||||
o/third_party/gcc/bin/$ARCH-linux-musl-addr2line "$@" 2>/dev/null && exit
|
||||
"$COSMO/o/third_party/gcc/bin/$ARCH-linux-musl-addr2line" "$@" 2>/dev/null && exit
|
||||
done
|
||||
|
||||
echo please set the ADDR2LINE environment variable >&2
|
||||
exit 1
|
|
@ -86,7 +86,7 @@ IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
|||
|
||||
ifneq ($(findstring aarch64,$(MODE)),)
|
||||
ARCH = aarch64
|
||||
HOSTS ?= pi silicon
|
||||
HOSTS ?= pi silicon-wifi
|
||||
else
|
||||
ARCH = x86_64
|
||||
HOSTS ?= freebsd rhel7 rhel5 xnu win10 openbsd netbsd
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
.PRECIOUS: o/$(MODE)/%.com.ok
|
||||
o/$(MODE)/%.com.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet
|
||||
o/$(MODE)/%.com.ok: \
|
||||
o//tool/build/runit.com \
|
||||
o//tool/build/runitd.com \
|
||||
o/$(MODE)/tool/build/runit.com \
|
||||
o/$(MODE)/tool/build/runitd.com \
|
||||
o/$(MODE)/%.com
|
||||
@$(COMPILE) -wATEST -tT$@ $^ $(HOSTS)
|
||||
$(COMPILE) -wATEST -tT$@ $^ $(HOSTS)
|
||||
|
||||
.PHONY:
|
||||
o/tiny/tool/build/runit.com:
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/core/core.h"
|
||||
#include "dsp/core/illumination.h"
|
||||
#include "dsp/core/core.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
const double kIlluminantA[3] = {1.0985, 1, 0.35585};
|
||||
|
|
|
@ -1080,12 +1080,12 @@ static plm_frame_t *plm_video_decode_impl(plm_video_t *self) {
|
|||
}
|
||||
|
||||
plm_frame_t *plm_video_decode(plm_video_t *self) {
|
||||
long double tsc;
|
||||
plm_frame_t *res;
|
||||
struct timespec tsc;
|
||||
INFOF("plm_video_decode");
|
||||
tsc = nowl();
|
||||
tsc = timespec_real();
|
||||
res = plm_video_decode_impl(self);
|
||||
plmpegdecode_latency_ = lroundl((nowl() - tsc) * 1e6l);
|
||||
plmpegdecode_latency_ = timespec_tomicros(timespec_sub(timespec_real(), tsc));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ static int ttysetcursor(int fd, bool visible) {
|
|||
char code[8] = "\e[?25l";
|
||||
if (__nocolor) return 0;
|
||||
if (visible) code[5] = 'h';
|
||||
if (SupportsWindows()) {
|
||||
if (IsWindows()) {
|
||||
GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);
|
||||
ntcursor.bVisible = visible;
|
||||
SetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);
|
||||
|
|
|
@ -7,36 +7,8 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* @fileoverview example of how to embed symbol table in .com file
|
||||
*
|
||||
* # build our binary
|
||||
* make -j16 o//examples/symtab.com
|
||||
*
|
||||
* # move binary somewhere else
|
||||
* # so it can't find the .com.dbg binary
|
||||
* cp o//examples/symtab.com /tmp
|
||||
*
|
||||
* # run program
|
||||
* # notice that it has a symbolic backtrace
|
||||
* /tmp/symtab.com
|
||||
*
|
||||
* @see examples/examples.mk
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// this links all the debugging and zip functionality
|
||||
ShowCrashReports();
|
||||
|
||||
kprintf("----------------\n");
|
||||
kprintf(" THIS IS A TEST \n");
|
||||
kprintf("SIMULATING CRASH\n");
|
||||
kprintf("----------------\n");
|
||||
|
||||
volatile int64_t x;
|
||||
return 1 / (x = 0);
|
||||
abort();
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
#include "libc/math.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* @fileoverview How to print backtraces and cpu state on crash.
|
||||
|
@ -38,6 +39,7 @@ dontubsan int main(int argc, char *argv[]) {
|
|||
volatile double c = exp(b) / a;
|
||||
(void)c;
|
||||
|
||||
volatile int64_t x;
|
||||
return 1 / (x = 0);
|
||||
volatile int x = 0;
|
||||
volatile int y = 1 / x;
|
||||
return y;
|
||||
}
|
||||
|
|
14
examples/die.c
Normal file
14
examples/die.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/log/log.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
__die();
|
||||
}
|
|
@ -55,6 +55,7 @@ EXAMPLES_DIRECTDEPS = \
|
|||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
|
@ -146,25 +147,6 @@ o/$(MODE)/examples/nesemu1.com.dbg: \
|
|||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
# # force symtab.com to be a zip file, by pulling a zip asset into linkage
|
||||
# # we wouldn't need to do this if we depended on functions like localtime
|
||||
# o/$(MODE)/examples/symtab.com.dbg: \
|
||||
# $(EXAMPLES_DEPS) \
|
||||
# o/$(MODE)/examples/symtab.o \
|
||||
# o/$(MODE)/examples/symtab.c.zip.o \
|
||||
# o/$(MODE)/examples/examples.pkg \
|
||||
# $(EXAMPLES_BOOTLOADER)
|
||||
# @$(APELINK)
|
||||
|
||||
# modify .com so it can read the symbol table without needing the .com.dbg file
|
||||
o/$(MODE)/examples/symtab.com: \
|
||||
o/$(MODE)/examples/symtab.com.dbg \
|
||||
o/$(MODE)/third_party/zip/zip.com \
|
||||
o/$(MODE)/tool/build/symtab.com
|
||||
@$(MAKE_OBJCOPY)
|
||||
@$(MAKE_SYMTAB_CREATE)
|
||||
@$(MAKE_SYMTAB_ZIP)
|
||||
|
||||
o/$(MODE)/examples/picol.o: private \
|
||||
CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libc/sock/struct/sockaddr.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/so.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
|
@ -116,9 +117,11 @@ void *Worker(void *id) {
|
|||
|
||||
server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server == -1) {
|
||||
kprintf("socket() failed %m\n"
|
||||
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
|
||||
threads * 2);
|
||||
kprintf("\r\e[Ksocket() failed %m\n");
|
||||
if (errno == ENFILE || errno == EMFILE) {
|
||||
TooManyFileDescriptors:
|
||||
kprintf("sudo prlimit --pid=$$ --nofile=%d\n", threads * 3);
|
||||
}
|
||||
goto WorkerFinished;
|
||||
}
|
||||
|
||||
|
@ -136,7 +139,7 @@ void *Worker(void *id) {
|
|||
// possible for our many threads to bind to the same interface!
|
||||
// otherwise we'd need to create a complex multi-threaded queue
|
||||
if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
kprintf("%s() failed %m\n", "socket");
|
||||
kprintf("\r\e[Ksocket() returned %m\n");
|
||||
goto CloseWorker;
|
||||
}
|
||||
unassert(!listen(server, 1));
|
||||
|
@ -148,7 +151,7 @@ void *Worker(void *id) {
|
|||
uint32_t clientaddrsize;
|
||||
struct sockaddr_in clientaddr;
|
||||
int client, inmsglen, outmsglen;
|
||||
char inbuf[1500], outbuf[512], *p, *q;
|
||||
char inbuf[512], outbuf[512], *p, *q;
|
||||
|
||||
// musl libc and cosmopolitan libc support a posix thread extension
|
||||
// that makes thread cancellation work much better your io routines
|
||||
|
@ -165,15 +168,14 @@ void *Worker(void *id) {
|
|||
// turns cancellation off so we don't interrupt active http clients
|
||||
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
|
||||
|
||||
// accept() can raise a very diverse number of errors but none of
|
||||
// them are really true showstoppers that would necessitate us to
|
||||
// panic and abort the entire server, so we can just ignore these
|
||||
if (client == -1) {
|
||||
// we used SO_RCVTIMEO and SO_SNDTIMEO because those settings are
|
||||
// inherited by the accepted sockets, but using them also has the
|
||||
// side-effect that the listening socket fails with EAGAIN errors
|
||||
// which are harmless, and so are most other errors accept raises
|
||||
// e.g. ECANCELED, which lets us check closingtime without delay!
|
||||
if (errno != EAGAIN && errno != ECANCELED) {
|
||||
kprintf("\r\e[Kaccept() returned %m\n");
|
||||
if (errno == ENFILE || errno == EMFILE) {
|
||||
goto TooManyFileDescriptors;
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -301,7 +303,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// print cpu registers and backtrace on crash
|
||||
// note that pledge'll makes backtraces worse
|
||||
// you can press ctrl+\ to trigger your crash
|
||||
// you can press ctrl+\ to trigger backtraces
|
||||
ShowCrashReports();
|
||||
|
||||
// listen for ctrl-c, terminal close, and kill
|
||||
|
@ -341,10 +343,6 @@ int main(int argc, char *argv[]) {
|
|||
// the server will be allowed to use. this way if it gets hacked, they
|
||||
// won't be able to do much damage, like compromising the whole server
|
||||
//
|
||||
// we use an internal api to force threads to enable beforehand, since
|
||||
// cosmopolitan code morphs the binary to support tls across platforms
|
||||
// and doing that requires extra permissions we don't need for serving
|
||||
//
|
||||
// pledge violations on openbsd are logged nicely to the system logger
|
||||
// but on linux we need to use a cosmopolitan extension to get details
|
||||
// although doing that slightly weakens the security pledge() provides
|
||||
|
@ -353,21 +351,22 @@ int main(int argc, char *argv[]) {
|
|||
// is too old, then pledge() and unveil() don't consider this an error
|
||||
// so it works. if security is critical there's a special call to test
|
||||
// which is npassert(!pledge(0, 0)), and npassert(unveil("", 0) != -1)
|
||||
__enable_threads();
|
||||
__pledge_mode = PLEDGE_PENALTY_KILL_THREAD | PLEDGE_STDERR_LOGGING;
|
||||
__pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; // c. greenbean --strace
|
||||
unveil("/dev/null", "rw");
|
||||
unveil(0, 0);
|
||||
pledge("stdio inet", 0);
|
||||
|
||||
// spawn over 9,000 worker threads
|
||||
// initialize our synchronization data structures, which were written
|
||||
// by mike burrows in a library called *nsync we've tailored for libc
|
||||
unassert(!pthread_cond_init(&statuscond, 0));
|
||||
unassert(!pthread_mutex_init(&statuslock, 0));
|
||||
|
||||
// spawn over 9000 worker threads
|
||||
//
|
||||
// you don't need weird i/o models, or event driven yoyo pattern code
|
||||
// to build a massively scalable server. the secret is to use threads
|
||||
// with tiny stacks. then you can write plain simple imperative code!
|
||||
//
|
||||
// we like pthread attributes since they generally make thread spawns
|
||||
// faster especially in cases where you need to make detached threads
|
||||
//
|
||||
// we block signals in our worker threads so we won't need messy code
|
||||
// to spin on eintr. operating systems also deliver signals to random
|
||||
// threads, and we'd have ctrl-c, etc. be handled by the main thread.
|
||||
|
@ -375,17 +374,15 @@ int main(int argc, char *argv[]) {
|
|||
// alternatively you can just use signal() instead of sigaction(); it
|
||||
// uses SA_RESTART because all the syscalls the worker currently uses
|
||||
// are documented as @restartable which means no EINTR toil is needed
|
||||
unassert(!pthread_cond_init(&statuscond, 0));
|
||||
unassert(!pthread_mutex_init(&statuslock, 0));
|
||||
sigset_t block;
|
||||
sigfillset(&block);
|
||||
sigdelset(&block, SIGSEGV); // invalid memory access
|
||||
sigdelset(&block, SIGBUS); // another kind of bad memory access
|
||||
sigdelset(&block, SIGFPE); // divide by zero, etc.
|
||||
sigdelset(&block, SIGSYS); // pledge violations
|
||||
sigdelset(&block, SIGILL); // bad cpu opcode
|
||||
sigemptyset(&block);
|
||||
sigaddset(&block, SIGINT);
|
||||
sigaddset(&block, SIGHUP);
|
||||
sigaddset(&block, SIGQUIT);
|
||||
pthread_attr_t attr;
|
||||
unassert(!pthread_attr_init(&attr));
|
||||
unassert(!pthread_attr_setguardsize(&attr, 4096));
|
||||
unassert(!pthread_attr_setstacksize(&attr, 65536));
|
||||
unassert(!pthread_attr_setsigmask_np(&attr, &block));
|
||||
pthread_t *th = gc(calloc(threads, sizeof(pthread_t)));
|
||||
for (i = 0; i < threads; ++i) {
|
||||
|
@ -393,10 +390,10 @@ int main(int argc, char *argv[]) {
|
|||
++a_workers;
|
||||
if ((rc = pthread_create(th + i, &attr, Worker, (void *)(intptr_t)i))) {
|
||||
--a_workers;
|
||||
// rc will most likely be EAGAIN (we hit the process/thread limit)
|
||||
kprintf("\r\e[Kerror: pthread_create(%d) failed: %s\n"
|
||||
" try increasing RLIMIT_NPROC\n",
|
||||
i, strerror(rc));
|
||||
kprintf("\r\e[Kpthread_create failed: %s\n", strerror(rc));
|
||||
if (rc == EAGAIN) {
|
||||
kprintf("sudo prlimit --pid=$$ --nproc=%d\n", threads * 2);
|
||||
}
|
||||
if (!i) exit(1);
|
||||
threads = i;
|
||||
break;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
/**
|
||||
* 16kb sleep executable that runs on all operating systems.
|
||||
* https://justine.lol/cosmopolitan/demos/sleep.c
|
||||
* https://justine.lol/cosmopolitan/demos/sleep.com
|
||||
* https://justine.lol/cosmopolitan/index.html
|
||||
*/
|
||||
|
||||
#define WRITE(s) write(2, s, strlen(s))
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *p;
|
||||
long double x, y;
|
||||
if (argc != 2) {
|
||||
WRITE("Usage: ");
|
||||
WRITE(argv[0]);
|
||||
WRITE(" SECONDS\n");
|
||||
exit(1);
|
||||
}
|
||||
x = 0;
|
||||
for (p = argv[1]; isdigit(*p); ++p) {
|
||||
x *= 10;
|
||||
x += *p - '0';
|
||||
}
|
||||
if (*p == '.') {
|
||||
y = 1;
|
||||
for (++p; isdigit(*p); ++p) {
|
||||
x += (*p - '0') * (y /= 10);
|
||||
}
|
||||
}
|
||||
switch (*p) {
|
||||
case 'm':
|
||||
x *= 60;
|
||||
break;
|
||||
case 'h':
|
||||
x *= 60 * 60;
|
||||
break;
|
||||
case 'd':
|
||||
x *= 60 * 60 * 24;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dsleep(x);
|
||||
return 0;
|
||||
}
|
|
@ -40,6 +40,7 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
|
@ -249,7 +250,7 @@ void Exit(int rc) {
|
|||
void Cleanup(void) {
|
||||
ttyraw((enum TtyRawFlags)(-1u));
|
||||
ttyshowcursor(STDOUT_FILENO);
|
||||
if (playpid_) kill(playpid_, SIGTERM), sched_yield();
|
||||
if (playpid_) kill(playpid_, SIGTERM), pthread_yield();
|
||||
}
|
||||
|
||||
void OnTimer(void) {
|
||||
|
|
139
examples/spawn_bench.c
Normal file
139
examples/spawn_bench.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
||||
#define ITERATIONS 10
|
||||
|
||||
_Alignas(128) int a;
|
||||
_Alignas(128) int b;
|
||||
_Alignas(128) atomic_int lock;
|
||||
|
||||
static struct timespec SubtractTime(struct timespec a, struct timespec b) {
|
||||
a.tv_sec -= b.tv_sec;
|
||||
if (a.tv_nsec < b.tv_nsec) {
|
||||
a.tv_nsec += 1000000000;
|
||||
a.tv_sec--;
|
||||
}
|
||||
a.tv_nsec -= b.tv_nsec;
|
||||
return a;
|
||||
}
|
||||
|
||||
static time_t ToNanoseconds(struct timespec ts) {
|
||||
return ts.tv_sec * 1000000000 + ts.tv_nsec;
|
||||
}
|
||||
|
||||
static char *Ithoa(char p[27], unsigned long x) {
|
||||
char m[26];
|
||||
unsigned i;
|
||||
i = 0;
|
||||
do {
|
||||
m[i++] = x % 10 + '0';
|
||||
x = x / 10;
|
||||
} while (x);
|
||||
for (;;) {
|
||||
*p++ = m[--i];
|
||||
if (!i) break;
|
||||
if (!(i % 3)) *p++ = ',';
|
||||
}
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
#define BENCH(name, x) \
|
||||
{ \
|
||||
int i; \
|
||||
char ibuf[27]; \
|
||||
struct timespec t1, t2; \
|
||||
clock_gettime(CLOCK_REALTIME, &t1); \
|
||||
for (i = 0; i < ITERATIONS; ++i) { \
|
||||
x; \
|
||||
} \
|
||||
clock_gettime(CLOCK_REALTIME, &t2); \
|
||||
Ithoa(ibuf, ToNanoseconds(SubtractTime(t2, t1)) / ITERATIONS); \
|
||||
printf("%-50s %16s ns\n", name, ibuf); \
|
||||
}
|
||||
|
||||
void PosixSpawnWait(const char *prog) {
|
||||
int ws, err, pid;
|
||||
char *args[] = {(char *)prog, 0};
|
||||
char *envs[] = {0};
|
||||
if ((err = posix_spawn(&pid, prog, NULL, NULL, args, envs))) {
|
||||
perror(prog);
|
||||
exit(1);
|
||||
}
|
||||
if (waitpid(pid, &ws, 0) == -1) {
|
||||
perror("waitpid");
|
||||
exit(1);
|
||||
}
|
||||
if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) {
|
||||
puts("bad exit status");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ForkExecWait(const char *prog) {
|
||||
int ws;
|
||||
if (!fork()) {
|
||||
execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0});
|
||||
_Exit(127);
|
||||
}
|
||||
if (wait(&ws) == -1) {
|
||||
perror("wait");
|
||||
exit(1);
|
||||
}
|
||||
if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) {
|
||||
puts("bad exit status");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
char *FillMemory(char *p, long n) {
|
||||
return memset(p, -1, n);
|
||||
}
|
||||
char *(*pFillMemory)(char *, long) = FillMemory;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
long n;
|
||||
void *p;
|
||||
const char *prog;
|
||||
|
||||
if (argc <= 1) {
|
||||
prog = "tiny64";
|
||||
} else {
|
||||
prog = argv[1];
|
||||
}
|
||||
|
||||
BENCH("fork() + exec(tiny)", ForkExecWait(prog));
|
||||
BENCH("posix_spawn(tiny)", PosixSpawnWait(prog));
|
||||
|
||||
n = 128L * 1024 * 1024;
|
||||
p = pFillMemory(
|
||||
mmap(0, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), n);
|
||||
BENCH("mmap(128mb, MAP_SHARED) + fork() + exec(tiny)", ForkExecWait(prog));
|
||||
BENCH("mmap(128mb, MAP_SHARED) + posix_spawn(tiny)", PosixSpawnWait(prog));
|
||||
munmap(p, n);
|
||||
|
||||
n = 128L * 1024 * 1024;
|
||||
p = pFillMemory(malloc(n), n);
|
||||
BENCH("malloc(128mb) + fork() + exec(tiny)", ForkExecWait(prog));
|
||||
BENCH("malloc(128mb) + posix_spawn(tiny)", PosixSpawnWait(prog));
|
||||
free(p);
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -21,8 +22,6 @@
|
|||
|
||||
#define N INT_MAX
|
||||
|
||||
STATIC_STACK_SIZE(FRAMESIZE);
|
||||
|
||||
int A(int f(), int n) {
|
||||
if (n < N) {
|
||||
return f(f, n + 1) - 1;
|
||||
|
@ -34,6 +33,10 @@ int A(int f(), int n) {
|
|||
int (*Ap)(int (*)(), int) = A;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (IsWindows()) {
|
||||
fprintf(stderr, "stack overflow not possible to catch on windows yet\n");
|
||||
exit(1);
|
||||
}
|
||||
ShowCrashReports();
|
||||
return !!Ap(Ap, 0);
|
||||
}
|
||||
|
|
|
@ -7,37 +7,13 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* @fileoverview shows how accurate clock_nanosleep() is
|
||||
*/
|
||||
void *hog(void) {
|
||||
return &__get_tls()->tib_errno;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
long i, ns;
|
||||
struct timespec x, y;
|
||||
timespec_sleep(timespec_fromnanos(0)); // warmup
|
||||
|
||||
printf("\nrelative sleep\n");
|
||||
for (i = 0; i < 28; ++i) {
|
||||
ns = 1l << i;
|
||||
x = timespec_real();
|
||||
timespec_sleep(timespec_fromnanos(ns));
|
||||
y = timespec_real();
|
||||
printf("%,11ld ns sleep took %,ld ns\n", ns,
|
||||
timespec_tonanos(timespec_sub(y, x)));
|
||||
}
|
||||
|
||||
printf("\nabsolute sleep\n");
|
||||
for (i = 0; i < 28; ++i) {
|
||||
ns = 1l << i;
|
||||
x = timespec_real();
|
||||
timespec_sleep_until(timespec_add(x, timespec_fromnanos(ns)));
|
||||
y = timespec_real();
|
||||
printf("%,11ld ns sleep took %,ld ns\n", ns,
|
||||
timespec_tonanos(timespec_sub(y, x)));
|
||||
}
|
||||
printf("%zu\n", sizeof(struct CosmoTib));
|
||||
}
|
100
examples/time.c
100
examples/time.c
|
@ -1,100 +0,0 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/x/xspawn.h"
|
||||
|
||||
/**
|
||||
* @fileoverview command for showing how long a command takes
|
||||
*
|
||||
* This offers the following improvements over the existing `time`
|
||||
* command that's incorporated into most shells:
|
||||
*
|
||||
* - This will show microseconds if seconds < 1
|
||||
* - This will launch APE binaries on systems with broken libc execv()
|
||||
*/
|
||||
|
||||
#define WRITE(FD, STR) write(FD, STR, strlen(STR))
|
||||
|
||||
void OnChild(void *arg) {
|
||||
char **argv = arg;
|
||||
execv(argv[0], argv);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
long double GetTimeval(struct timeval t) {
|
||||
return t.tv_sec + t.tv_usec * 1e-6l;
|
||||
}
|
||||
|
||||
void PrintMetric(const char *name, long double d) {
|
||||
char buf[256], *p = buf;
|
||||
long mins, secs, mils, mics;
|
||||
mins = d / 60;
|
||||
secs = fmodl(d, 60);
|
||||
mils = fmodl(d * 1000, 1000);
|
||||
mics = fmodl(d * 1000000, 1000);
|
||||
p = stpcpy(p, name), *p++ = '\t';
|
||||
p = FormatInt64(p, mins), *p++ = 'm';
|
||||
p = FormatInt64(p, secs), *p++ = '.';
|
||||
*p++ = '0' + mils / 100;
|
||||
*p++ = '0' + mils / 10 % 10;
|
||||
*p++ = '0' + mils % 10;
|
||||
if (!secs) {
|
||||
*p++ = '0' + mics / 100;
|
||||
*p++ = '0' + mics / 10 % 10;
|
||||
*p++ = '0' + mics % 10;
|
||||
}
|
||||
*p++ = 's';
|
||||
*p++ = '\n';
|
||||
write(2, buf, p - buf);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int ws;
|
||||
char *exepath;
|
||||
struct rusage r;
|
||||
long double real;
|
||||
char exebuf[PATH_MAX];
|
||||
if (argc >= 2) {
|
||||
if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) {
|
||||
real = nowl();
|
||||
argv[1] = exepath;
|
||||
if ((ws = xvspawn(OnChild, argv + 1, &r)) != -1) {
|
||||
PrintMetric("real", nowl() - real);
|
||||
PrintMetric("user", GetTimeval(r.ru_utime));
|
||||
PrintMetric("sys", GetTimeval(r.ru_stime));
|
||||
if (WIFEXITED(ws)) {
|
||||
return WEXITSTATUS(ws);
|
||||
} else {
|
||||
return 128 + WTERMSIG(ws);
|
||||
}
|
||||
} else {
|
||||
perror("xvspawn");
|
||||
return 127;
|
||||
}
|
||||
} else {
|
||||
perror(argv[1]);
|
||||
return 127;
|
||||
}
|
||||
} else {
|
||||
WRITE(2, "Usage: ");
|
||||
WRITE(2, argv[0]);
|
||||
WRITE(2, " PROG [ARGS...]\n");
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
|
@ -90,9 +90,9 @@ int rawmode(void) {
|
|||
perror("tcsetattr");
|
||||
}
|
||||
|
||||
WRITE(1, ENABLE_SAFE_PASTE);
|
||||
WRITE(1, ENABLE_MOUSE_TRACKING);
|
||||
WRITE(1, PROBE_DISPLAY_SIZE);
|
||||
/* WRITE(1, ENABLE_SAFE_PASTE); */
|
||||
/* WRITE(1, ENABLE_MOUSE_TRACKING); */
|
||||
/* WRITE(1, PROBE_DISPLAY_SIZE); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ const char *describemouseevent(int e) {
|
|||
// change the code above to enable ISIG if you want to trigger this
|
||||
// then press ctrl-c or ctrl-\ in your pseudoteletypewriter console
|
||||
void OnSignalThatWontEintrRead(int sig) {
|
||||
dprintf(1, "got %s\n", strsignal(sig));
|
||||
dprintf(1, "got %s (read()ing will SA_RESTART)\n", strsignal(sig));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
@ -170,6 +170,10 @@ int main(int argc, char *argv[]) {
|
|||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
if (!n) {
|
||||
printf("got stdin eof\n");
|
||||
exit(0);
|
||||
}
|
||||
printf("%`'.*s (got %d) ", n, code, n);
|
||||
if (iscntrl(code[0]) && !code[1]) {
|
||||
printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0]));
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/**
|
||||
* Returns true if signal is ignored by default.
|
||||
*/
|
||||
textwindows bool __sig_is_ignored(int sig) {
|
||||
return sig == SIGURG || //
|
||||
sig == SIGCONT || //
|
||||
sig == SIGCHLD || //
|
||||
sig == SIGWINCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if signal is so fatal it should dump core.
|
||||
*/
|
||||
textwindows bool __sig_is_core(int sig) {
|
||||
return sig == SIGSYS || //
|
||||
sig == SIGBUS || //
|
||||
sig == SIGSEGV || //
|
||||
sig == SIGQUIT || //
|
||||
sig == SIGTRAP || //
|
||||
sig == SIGXCPU || //
|
||||
sig == SIGXFSZ;
|
||||
}
|
||||
|
||||
static inline textwindows int __sig_is_masked(int sig) {
|
||||
if (__tls_enabled) {
|
||||
return __get_tls()->tib_sigmask & (1ull << (sig - 1));
|
||||
} else {
|
||||
return __sig.mask & (1ull << (sig - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers signal to callback.
|
||||
*
|
||||
* @return true if `EINTR` should be raised
|
||||
*/
|
||||
bool __sig_deliver(int sigops, int sig, int sic, ucontext_t *ctx) {
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
unsigned flags = __sighandflags[sig];
|
||||
|
||||
// generate expensive data if needed
|
||||
ucontext_t uc;
|
||||
siginfo_t info;
|
||||
siginfo_t *infop;
|
||||
if (flags & SA_SIGINFO) {
|
||||
__repstosb(&info, 0, sizeof(info));
|
||||
info.si_signo = sig;
|
||||
info.si_code = sic;
|
||||
infop = &info;
|
||||
if (!ctx) {
|
||||
struct NtContext nc = {.ContextFlags = kNtContextAll};
|
||||
__repstosb(&uc, 0, sizeof(uc));
|
||||
GetThreadContext(GetCurrentThread(), &nc);
|
||||
_ntcontext2linux(&uc, &nc);
|
||||
ctx = &uc;
|
||||
}
|
||||
} else {
|
||||
infop = 0;
|
||||
}
|
||||
|
||||
// save the thread's signal mask
|
||||
uint64_t oldmask;
|
||||
if (__tls_enabled) {
|
||||
oldmask = __get_tls()->tib_sigmask;
|
||||
} else {
|
||||
oldmask = __sig.mask;
|
||||
}
|
||||
if (ctx) {
|
||||
ctx->uc_sigmask = (sigset_t){{oldmask}};
|
||||
}
|
||||
|
||||
// mask the signal that's being handled whilst handling
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_sigmask |= 1ull << (sig - 1);
|
||||
} else {
|
||||
__sig.mask |= 1ull << (sig - 1);
|
||||
}
|
||||
}
|
||||
|
||||
STRACE("delivering %G", sig);
|
||||
((sigaction_f)(__executable_start + rva))(sig, infop, ctx);
|
||||
|
||||
if (ctx) {
|
||||
oldmask = ctx->uc_sigmask.__bits[0];
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_sigmask = oldmask;
|
||||
} else {
|
||||
__sig.mask = oldmask;
|
||||
}
|
||||
if (flags & SA_RESETHAND) {
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
if (!(sigops & kSigOpRestartable)) {
|
||||
return true; // always send EINTR for wait4(), poll(), etc.
|
||||
} else if (flags & SA_RESTART) {
|
||||
STRACE("restarting syscall on %G", sig);
|
||||
return false; // resume syscall for read(), write(), etc.
|
||||
} else {
|
||||
return true; // default course is to raise EINTR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signal.
|
||||
* @return true if `EINTR` should be raised
|
||||
*/
|
||||
textwindows bool __sig_handle(int sigops, int sig, int sic, ucontext_t *ctx) {
|
||||
if (__sighandrvas[sig] == (intptr_t)SIG_IGN ||
|
||||
(__sighandrvas[sig] == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) {
|
||||
return false;
|
||||
}
|
||||
if (__sig_is_masked(sig)) {
|
||||
if (sigops & kSigOpUnmaskable) {
|
||||
goto DefaultAction;
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_sigpending |= 1ull << (sig - 1);
|
||||
} else {
|
||||
__sig.pending |= 1ull << (sig - 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
switch (__sighandrvas[sig]) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
DefaultAction:
|
||||
if (!__sig_is_ignored(sig)) {
|
||||
uint32_t cmode;
|
||||
intptr_t hStderr;
|
||||
const char *signame;
|
||||
char *end, sigbuf[21], output[123];
|
||||
signame = strsignal_r(sig, sigbuf);
|
||||
STRACE("terminating due to uncaught %s", signame);
|
||||
if (__sig_is_core(sig)) {
|
||||
hStderr = GetStdHandle(kNtStdErrorHandle);
|
||||
if (GetConsoleMode(hStderr, &cmode)) {
|
||||
end = stpcpy(output, signame);
|
||||
end = stpcpy(end, " ");
|
||||
end = stpcpy(
|
||||
end,
|
||||
DescribeBacktrace(
|
||||
ctx ? (struct StackFrame *)ctx->uc_mcontext.BP
|
||||
: (struct StackFrame *)__builtin_frame_address(0)));
|
||||
end = stpcpy(end, "\n");
|
||||
WriteFile(hStderr, output, end - output, 0, 0);
|
||||
}
|
||||
}
|
||||
ExitProcess(sig);
|
||||
}
|
||||
// fallthrough
|
||||
case (intptr_t)SIG_IGN:
|
||||
return false;
|
||||
default:
|
||||
return __sig_deliver(sigops, sig, sic, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows bool __sig_checkem(int sigops, uint64_t *pending) {
|
||||
bool res = false;
|
||||
for (int sig = 1; sig <= 64; ++sig) {
|
||||
if (*pending & (1ull << (sig - 1))) {
|
||||
*pending &= ~(1ull << (sig - 1));
|
||||
res |= __sig_handle(sigops, sig, SI_KERNEL, 0);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for unblocked signals and delivers them on New Technology.
|
||||
* @return true if EINTR should be returned by caller
|
||||
* @note called from main thread
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows bool __sig_check(int sigops) {
|
||||
bool res = false;
|
||||
if (__tls_enabled) {
|
||||
res |= __sig_checkem(sigops, &__get_tls()->tib_sigpending);
|
||||
}
|
||||
return res | __sig_checkem(sigops, &__sig.pending);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -40,5 +40,5 @@ wontreturn void abort(void) {
|
|||
raise(SIGABRT);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
notpossible;
|
||||
_Exit(128 + SIGABRT);
|
||||
}
|
||||
|
|
|
@ -1,25 +1,10 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
|
||||
#include "libc/dce.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int begin_blocking_operation(void);
|
||||
void end_blocking_operation(int);
|
||||
|
||||
#if SupportsWindows()
|
||||
#define BEGIN_BLOCKING_OPERATION \
|
||||
do { \
|
||||
int _Flags; \
|
||||
_Flags = begin_blocking_operation()
|
||||
#define END_BLOCKING_OPERATION \
|
||||
end_blocking_operation(_Flags); \
|
||||
} \
|
||||
while (0)
|
||||
#else
|
||||
#define BEGIN_BLOCKING_OPERATION (void)0
|
||||
#define END_BLOCKING_OPERATION (void)0
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -209,6 +209,7 @@ int madvise(void *, uint64_t, int);
|
|||
#endif
|
||||
|
||||
#ifdef _COSMO_SOURCE
|
||||
bool32 fdexists(int);
|
||||
bool32 fileexists(const char *);
|
||||
bool32 isdirectory(const char *);
|
||||
bool32 isexecutable(const char *);
|
||||
|
@ -232,8 +233,6 @@ int sys_ptrace(int, ...);
|
|||
int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
|
||||
int sys_ioprio_get(int, int);
|
||||
int sys_ioprio_set(int, int, int);
|
||||
int tgkill(int, int, int);
|
||||
int tkill(int, int);
|
||||
int tmpfd(void);
|
||||
int touch(const char *, unsigned);
|
||||
int unveil(const char *, const char *);
|
||||
|
|
|
@ -65,53 +65,11 @@ $(LIBC_CALLS_A).pkg: \
|
|||
$(LIBC_CALLS_A_OBJS) \
|
||||
$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
# we can't use sanitizers because:
|
||||
# we're on a stack owned by win32 without tls
|
||||
o/$(MODE)/libc/calls/foist.o \
|
||||
o/$(MODE)/libc/calls/__sig2.o \
|
||||
o/$(MODE)/libc/calls/sigchld-nt.o \
|
||||
o/$(MODE)/libc/calls/sigwinch-nt.o \
|
||||
o/$(MODE)/libc/calls/onntconsoleevent.o \
|
||||
o/$(MODE)/libc/calls/wincrash.o \
|
||||
o/$(MODE)/libc/calls/ntcontext2linux.o: private \
|
||||
$(LIBC_CALLS_A_OBJS): private \
|
||||
COPTS += \
|
||||
$(NO_MAGIC)
|
||||
|
||||
# we can't use asan because:
|
||||
# siginfo_t memory is owned by kernels
|
||||
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
|
||||
COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use asan because:
|
||||
# ucontext_t memory is owned by xnu kernel
|
||||
o/$(MODE)/libc/calls/sigenter-xnu.o: private \
|
||||
COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use asan because:
|
||||
# vdso memory is owned by linux kernel
|
||||
o/$(MODE)/libc/calls/vdsofunc.greg.o: private \
|
||||
COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use magic because:
|
||||
# this code is called by WinMain
|
||||
o/$(MODE)/libc/calls/winstdin1.o: private \
|
||||
COPTS += \
|
||||
$(NO_MAGIC)
|
||||
|
||||
# we can't use asan because:
|
||||
# ntspawn allocates 128kb of heap memory via win32
|
||||
o/$(MODE)/libc/calls/ntspawn.o \
|
||||
o/$(MODE)/libc/calls/mkntcmdline.o \
|
||||
o/$(MODE)/libc/calls/mkntenvblock.o: private \
|
||||
COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
-fno-sanitize=all \
|
||||
-Wframe-larger-than=4096 \
|
||||
-Walloca-larger-than=4096
|
||||
|
||||
ifneq ($(ARCH), aarch64)
|
||||
# we always want -O3 because:
|
||||
|
@ -129,21 +87,6 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: private \
|
|||
-mstringop-strategy=loop
|
||||
endif
|
||||
|
||||
# we must disable static stack safety because:
|
||||
# these functions use alloca(n)
|
||||
o/$(MODE)/libc/calls/execl.o \
|
||||
o/$(MODE)/libc/calls/execle.o \
|
||||
o/$(MODE)/libc/calls/execlp.o \
|
||||
o/$(MODE)/libc/calls/execvpe.o \
|
||||
o/$(MODE)/libc/calls/statfs.o \
|
||||
o/$(MODE)/libc/calls/fstatfs.o \
|
||||
o/$(MODE)/libc/calls/execve-sysv.o \
|
||||
o/$(MODE)/libc/calls/readlinkat-nt.o \
|
||||
o/$(MODE)/libc/calls/execve-nt.greg.o \
|
||||
o/$(MODE)/libc/calls/mkntenvblock.o: private \
|
||||
CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
# we always want -Os because:
|
||||
# va_arg codegen is very bloated in default mode
|
||||
o//libc/calls/open.o \
|
||||
|
@ -176,24 +119,6 @@ o/$(MODE)/libc/calls/timeval_frommicros.o: private \
|
|||
CFLAGS += \
|
||||
-O2
|
||||
|
||||
# privileged functions
|
||||
o/$(MODE)/libc/calls/sigenter-freebsd.o \
|
||||
o/$(MODE)/libc/calls/sigenter-netbsd.o \
|
||||
o/$(MODE)/libc/calls/sigenter-openbsd.o \
|
||||
o/$(MODE)/libc/calls/sigenter-linux.o \
|
||||
o/$(MODE)/libc/calls/sigenter-xnu.o \
|
||||
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
|
||||
CFLAGS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=all \
|
||||
-fno-stack-protector
|
||||
|
||||
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||
o/$(MODE)/libc/calls/unveil.o: private \
|
||||
CFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
o/$(MODE)/libc/calls/sigaction.o: private CFLAGS += -mcmodel=large
|
||||
o/$(MODE)/libc/calls/getloadavg-nt.o: private CFLAGS += -ffreestanding
|
||||
|
@ -215,7 +140,7 @@ o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
|
|||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/switchstacks.o: libc/calls/switchstacks.S
|
||||
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -49,7 +50,15 @@
|
|||
int chdir(const char *path) {
|
||||
int rc;
|
||||
struct ZiposUri zipname;
|
||||
GetProgramExecutableName(); // XXX: ugly workaround
|
||||
if (_weaken(__get_tmpdir)) {
|
||||
_weaken(__get_tmpdir)();
|
||||
}
|
||||
if (_weaken(GetAddr2linePath)) {
|
||||
_weaken(GetAddr2linePath)();
|
||||
}
|
||||
if (_weaken(GetProgramExecutableName)) {
|
||||
_weaken(GetProgramExecutableName)();
|
||||
}
|
||||
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_parseuri) &&
|
||||
|
|
|
@ -22,5 +22,5 @@
|
|||
#include "libc/runtime/syslib.internal.h"
|
||||
|
||||
int sys_clock_gettime_m1(int clock, struct timespec *ts) {
|
||||
return _sysret(__syslib->clock_gettime(clock, ts));
|
||||
return _sysret(__syslib->__clock_gettime(clock, ts));
|
||||
}
|
||||
|
|
|
@ -77,18 +77,17 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
int clock_gettime(int clock, struct timespec *ts) {
|
||||
// threads on win32 stacks call this so we can't asan check *ts
|
||||
int rc;
|
||||
if (clock == 127) {
|
||||
rc = einval(); // 127 is used by consts.sh to mean unsupported
|
||||
} else if (!ts || (IsAsan() && !__asan_is_valid_timespec(ts))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
rc = __clock_gettime(clock, ts);
|
||||
}
|
||||
#if SYSDEBUG
|
||||
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
|
||||
DescribeTimespec(rc, ts), rc);
|
||||
POLLTRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
|
||||
DescribeTimespec(rc, ts), rc);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
|
|
|
@ -16,43 +16,32 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/finger/finger.h"
|
||||
|
||||
static textwindows int sys_clock_nanosleep_nt_impl(int clock, int flags,
|
||||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
struct timespec now, abs;
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = *req;
|
||||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0)) return -1;
|
||||
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||
timespec_tomillis(timespec_sub(abs, now))),
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
|
||||
struct timespec abs) {
|
||||
struct timespec now;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
abs = timespec_add(now, *req);
|
||||
for (;;) {
|
||||
sys_clock_gettime_nt(clock, &now);
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0)) {
|
||||
if (rem) *rem = timespec_sub(abs, now);
|
||||
return -1;
|
||||
}
|
||||
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||
timespec_tomillis(timespec_sub(abs, now))),
|
||||
false);
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0)) return -1;
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore,
|
||||
timespec_tomillis(timespec_sub(abs, now)));
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +50,17 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
|||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int rc;
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
rc = sys_clock_nanosleep_nt_impl(clock, flags, req, rem);
|
||||
END_BLOCKING_OPERATION;
|
||||
struct timespec abs, now;
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = *req;
|
||||
} else {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
abs = timespec_add(now, *req);
|
||||
}
|
||||
rc = sys_clock_nanosleep_nt_impl(clock, abs);
|
||||
if (rc == -1 && rem && errno == EINTR) {
|
||||
sys_clock_gettime_nt(clock, &now);
|
||||
*rem = timespec_subz(abs, now);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -21,12 +21,18 @@
|
|||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
|
@ -51,16 +57,25 @@ int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
|
|||
#else
|
||||
long res;
|
||||
struct timespec abs, now, rel;
|
||||
if (_weaken(pthread_testcancel_np) && //
|
||||
_weaken(pthread_testcancel_np)()) {
|
||||
return ecanceled();
|
||||
}
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = *req;
|
||||
if (!(res = __syslib->clock_gettime(clock, &now))) {
|
||||
if (!(res = __syslib->__clock_gettime(clock, &now))) {
|
||||
if (timespec_cmp(abs, now) > 0) {
|
||||
rel = timespec_sub(abs, now);
|
||||
res = __syslib->nanosleep(&rel, 0);
|
||||
res = __syslib->__nanosleep(&rel, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = __syslib->nanosleep(req, rem);
|
||||
res = __syslib->__nanosleep(req, rem);
|
||||
}
|
||||
if (res == -EINTR && //
|
||||
(_weaken(pthread_testcancel_np) && //
|
||||
_weaken(pthread_testcancel_np))) {
|
||||
return ecanceled();
|
||||
}
|
||||
return _sysret(res);
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/clock_gettime.internal.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
|
@ -78,7 +79,7 @@ static struct timespec GetNanosleepLatency(void) {
|
|||
clock_gettime_f *cgt;
|
||||
struct timespec x, y, w = {0, 1};
|
||||
if (!(nanos = g_nanosleep_latency)) {
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_SIGNALS;
|
||||
for (cgt = __clock_gettime_get(0);;) {
|
||||
npassert(!cgt(CLOCK_REALTIME_PRECISE, &x));
|
||||
rc = sys_clock_nanosleep(CLOCK_REALTIME, 0, &w, 0);
|
||||
|
@ -90,7 +91,7 @@ static struct timespec GetNanosleepLatency(void) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
}
|
||||
return timespec_fromnanos(nanos);
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ static errno_t SpinNanosleep(int clock, int flags, const struct timespec *req,
|
|||
cgt = __clock_gettime_get(0);
|
||||
npassert(!cgt(CLOCK_REALTIME, &start));
|
||||
for (;;) {
|
||||
sched_yield();
|
||||
pthread_yield();
|
||||
npassert(!cgt(CLOCK_REALTIME, &now));
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
if (timespec_cmp(now, *req) >= 0) {
|
||||
|
@ -242,13 +243,11 @@ static bool ShouldUseSpinNanosleep(int clock, int flags,
|
|||
errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int rc;
|
||||
// threads on win32 stacks call this so we can't asan check *ts
|
||||
LOCKTRACE("clock_nanosleep(%s, %s, %s) → ...", DescribeClockName(clock),
|
||||
DescribeSleepFlags(flags), DescribeTimespec(0, req));
|
||||
if (IsMetal()) {
|
||||
rc = ENOSYS;
|
||||
} else if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
|
||||
(rem && !__asan_is_valid_timespec(rem))))) {
|
||||
rc = EFAULT;
|
||||
} else if (clock == 127 || //
|
||||
(flags & ~TIMER_ABSTIME) || //
|
||||
req->tv_sec < 0 || //
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/madv.h"
|
||||
|
@ -37,17 +38,24 @@
|
|||
|
||||
static textwindows int sys_copyfile_nt(const char *src, const char *dst,
|
||||
int flags) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct {
|
||||
char16_t src16[PATH_MAX];
|
||||
char16_t dst16[PATH_MAX];
|
||||
} M;
|
||||
CheckLargeStackAllocation(&M, sizeof(M));
|
||||
#pragma GCC pop_options
|
||||
int64_t fhsrc, fhdst;
|
||||
struct NtFileTime accessed, modified;
|
||||
char16_t src16[PATH_MAX], dst16[PATH_MAX];
|
||||
if (__mkntpath(src, src16) == -1) return -1;
|
||||
if (__mkntpath(dst, dst16) == -1) return -1;
|
||||
if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) {
|
||||
if (__mkntpath(src, M.src16) == -1) return -1;
|
||||
if (__mkntpath(dst, M.dst16) == -1) return -1;
|
||||
if (CopyFile(M.src16, M.dst16, !!(flags & COPYFILE_NOCLOBBER))) {
|
||||
if (flags & COPYFILE_PRESERVE_TIMESTAMPS) {
|
||||
fhsrc = CreateFile(src16, kNtFileReadAttributes, kNtFileShareRead, NULL,
|
||||
kNtOpenExisting, kNtFileAttributeNormal, 0);
|
||||
fhdst = CreateFile(dst16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
|
||||
fhsrc = CreateFile(M.src16, kNtFileReadAttributes, kNtFileShareRead, NULL,
|
||||
kNtOpenExisting, kNtFileAttributeNormal, 0);
|
||||
fhdst = CreateFile(M.dst16, kNtFileWriteAttributes, kNtFileShareRead,
|
||||
NULL, kNtOpenExisting, kNtFileAttributeNormal, 0);
|
||||
if (fhsrc != -1 && fhdst != -1) {
|
||||
GetFileTime(fhsrc, NULL, &accessed, &modified);
|
||||
SetFileTime(fhdst, NULL, &accessed, &modified);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
|
||||
static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
|
||||
static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
|
||||
char t;
|
||||
size_t a, b, i = 0;
|
||||
do {
|
||||
|
@ -38,7 +38,7 @@ static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
|
|||
}
|
||||
|
||||
// This function is called very early by WinMain().
|
||||
dontasan textwindows char16_t *__create_pipe_name(char16_t *a) {
|
||||
textwindows char16_t *__create_pipe_name(char16_t *a) {
|
||||
char16_t *p = a;
|
||||
const char *q = "\\\\?\\pipe\\cosmo\\";
|
||||
static atomic_uint x;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
// TODO(jart): DELETE
|
||||
|
||||
/**
|
||||
* Returns seconds since epoch w/ high-precision.
|
||||
* @param clockid can be CLOCK_{REALTIME,MONOTONIC}, etc.
|
||||
*/
|
||||
long double dtime(int clockid) {
|
||||
long double secs;
|
||||
struct timespec tv;
|
||||
clock_gettime(clockid, &tv);
|
||||
secs = tv.tv_nsec;
|
||||
secs *= 1e-9;
|
||||
secs += tv.tv_sec;
|
||||
return secs;
|
||||
}
|
|
@ -55,6 +55,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
|||
}
|
||||
if (g_fds.p[newfd].kind) {
|
||||
sys_close_nt(g_fds.p + newfd, newfd);
|
||||
bzero(g_fds.p + newfd, sizeof(*g_fds.p));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,8 +167,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
|||
}
|
||||
|
||||
bool32 ok;
|
||||
struct NtOverlapped ov = {.hEvent = f->handle,
|
||||
.Pointer = (void *)(uintptr_t)off};
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = off};
|
||||
|
||||
if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) {
|
||||
|
||||
|
@ -254,8 +253,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
|||
// allow a big range to unlock many small ranges
|
||||
for (flp = &g_locks.list, fl = *flp; fl;) {
|
||||
if (fl->fd == fd && EncompassesFileLock(fl, off, len)) {
|
||||
struct NtOverlapped ov = {.hEvent = f->handle,
|
||||
.Pointer = (void *)(uintptr_t)fl->off};
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
|
||||
if (UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
|
||||
*flp = fl->next;
|
||||
ft = fl->next;
|
||||
|
@ -287,14 +285,13 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
|||
off + len >= fl->off && //
|
||||
off + len < fl->off + fl->len) {
|
||||
// cleave left side of lock
|
||||
struct NtOverlapped ov = {.hEvent = f->handle,
|
||||
.Pointer = (void *)(uintptr_t)fl->off};
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
|
||||
if (!UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
|
||||
return -1;
|
||||
}
|
||||
fl->len = (fl->off + fl->len) - (off + len);
|
||||
fl->off = off + len;
|
||||
ov.Pointer = (void *)(uintptr_t)fl->off;
|
||||
ov.Pointer = fl->off;
|
||||
if (!LockFileEx(f->handle, kNtLockfileExclusiveLock, 0, fl->len,
|
||||
fl->len >> 32, &ov)) {
|
||||
return -1;
|
||||
|
|
51
libc/calls/fdexists.c
Normal file
51
libc/calls/fdexists.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
|
||||
/**
|
||||
* Returns true if file descriptor exists.
|
||||
*
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* fcntl(fd, F_GETFL) != -1
|
||||
*
|
||||
* Except it won't clobber `errno` and has minimal linkage.
|
||||
*/
|
||||
bool32 fdexists(int fd) {
|
||||
bool32 res;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
int e = errno;
|
||||
if (__sys_fcntl(fd, F_GETFL) != -1) {
|
||||
res = true;
|
||||
} else {
|
||||
errno = e;
|
||||
res = false;
|
||||
}
|
||||
} else {
|
||||
res = __isfdopen(fd);
|
||||
}
|
||||
STRACE("fdexists(%d) → %hhhd", fd, res);
|
||||
return res;
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/execve.internal.h"
|
||||
#include "libc/proc/execve.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/nt/enum/threadaccess.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
// WIN32 doesn't have the System V red-zone. Microsoft says they reserve
|
||||
// the right to trample all over it. so we technically don't need to use
|
||||
// this value. it's just not clear how common it is for WIN32 to clobber
|
||||
// the red zone, which means broken code could seem to mostly work which
|
||||
// means it's better that we're not the ones responsible for breaking it
|
||||
#define kRedzoneSize 128
|
||||
|
||||
// Both Microsoft and the Fifth Bell System agree on this one.
|
||||
#define kStackAlign 16
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
|
||||
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||
__msabi extern typeof(GetThreadContext) *const __imp_GetThreadContext;
|
||||
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
|
||||
__msabi extern typeof(ResumeThread) *const __imp_ResumeThread;
|
||||
__msabi extern typeof(SetThreadContext) *const __imp_SetThreadContext;
|
||||
__msabi extern typeof(SuspendThread) *const __imp_SuspendThread;
|
||||
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
|
||||
|
||||
int WinThreadLaunch(struct Delivery *, long, int (*)(struct Delivery *), long);
|
||||
|
||||
static textwindows unsigned long StrLen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static textwindows void Log(const char *s) {
|
||||
#if IsModeDbg()
|
||||
__imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes signal handler asynchronously inside other thread.
|
||||
*
|
||||
* @return 0 on success, or -1 on error
|
||||
*/
|
||||
textwindows int _pthread_signal(struct PosixThread *pt, int sig, int sic) {
|
||||
int rc = -1;
|
||||
intptr_t th;
|
||||
if ((th = __imp_OpenThread(
|
||||
kNtThreadSuspendResume | kNtThreadGetContext, false,
|
||||
atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)))) {
|
||||
uint32_t old_suspend_count;
|
||||
if ((old_suspend_count = __imp_SuspendThread(th)) != -1u) {
|
||||
if (!old_suspend_count &&
|
||||
atomic_load_explicit(&pt->status, memory_order_acquire) <
|
||||
kPosixThreadTerminated) {
|
||||
struct NtContext nc = {.ContextFlags = kNtContextAll};
|
||||
struct Delivery pkg = {0, sig, sic, &nc};
|
||||
if (__imp_GetThreadContext(th, &nc)) {
|
||||
struct CosmoTib *mytls;
|
||||
mytls = __get_tls();
|
||||
__set_tls_win32(pt->tib);
|
||||
rc = WinThreadLaunch(
|
||||
&pkg, 0, __sig_tramp,
|
||||
ROUNDDOWN(nc.Rsp - kRedzoneSize, kStackAlign) - 8);
|
||||
__imp_SetThreadContext(th, &nc);
|
||||
__set_tls_win32(mytls);
|
||||
} else {
|
||||
Log("GetThreadContext failed\n");
|
||||
}
|
||||
}
|
||||
__imp_ResumeThread(th);
|
||||
} else {
|
||||
Log("SuspendThread failed\n");
|
||||
}
|
||||
__imp_CloseHandle(th);
|
||||
} else {
|
||||
Log("OpenThread failed\n");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -122,7 +122,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
|
|||
} else {
|
||||
st.st_ctim = st.st_mtim;
|
||||
}
|
||||
st.st_gid = st.st_uid = __synthesize_uid();
|
||||
st.st_gid = st.st_uid = sys_getuid_nt();
|
||||
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
||||
st.st_dev = wst.dwVolumeSerialNumber;
|
||||
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
|
||||
|
|
|
@ -35,10 +35,13 @@
|
|||
* @cancellationpoint
|
||||
*/
|
||||
int fstatfs(int fd, struct statfs *sf) {
|
||||
int rc;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
union statfs_meta m;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
CheckLargeStackAllocation(&m, sizeof(m));
|
||||
#pragma GCC pop_options
|
||||
int rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotsup();
|
||||
|
|
|
@ -16,14 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/rlimit.h"
|
||||
|
||||
/**
|
||||
* Gets file descriptor table size.
|
||||
*
|
||||
* @return current limit on the number of open files per process
|
||||
* Returns limit on number of open files.
|
||||
*/
|
||||
int getdtablesize(void) {
|
||||
return g_fds.n;
|
||||
struct rlimit rl = {10000};
|
||||
getrlimit(RLIMIT_NOFILE, &rl);
|
||||
return MIN(rl.rlim_cur, INT_MAX);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
|
@ -30,124 +32,113 @@
|
|||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
|
||||
#define SIZE 1024
|
||||
#define CTL_KERN 1
|
||||
#define KERN_PROC 14
|
||||
#define KERN_PROC_PATHNAME_FREEBSD 12
|
||||
#define KERN_PROC_PATHNAME_NETBSD 5
|
||||
|
||||
static struct ProgramExecutableName {
|
||||
_Atomic(uint32_t) once;
|
||||
char buf[PATH_MAX];
|
||||
} program_executable_name;
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
union {
|
||||
char buf[PATH_MAX];
|
||||
char16_t buf16[PATH_MAX / 2];
|
||||
} u;
|
||||
} g_prog;
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static inline char *StrCat(char buf[PATH_MAX], const char *a, const char *b) {
|
||||
char *p, *e;
|
||||
p = buf;
|
||||
e = buf + PATH_MAX;
|
||||
while (*a && p < e) *p++ = *a++;
|
||||
while (*b && p < e) *p++ = *b++;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
||||
int c;
|
||||
char *q;
|
||||
char **ep;
|
||||
ssize_t rc;
|
||||
size_t i, n;
|
||||
union {
|
||||
int cmd[4];
|
||||
char path[PATH_MAX];
|
||||
char16_t path16[PATH_MAX];
|
||||
} u;
|
||||
static inline void InitProgramExecutableNameImpl(void) {
|
||||
|
||||
if (IsWindows()) {
|
||||
n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16));
|
||||
for (i = 0; i < n; ++i) {
|
||||
int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// turn c:\foo\bar into c:/foo/bar
|
||||
if (u.path16[i] == '\\') {
|
||||
u.path16[i] = '/';
|
||||
if (g_prog.u.buf16[i] == '\\') {
|
||||
g_prog.u.buf16[i] = '/';
|
||||
}
|
||||
}
|
||||
if (IsAlpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') {
|
||||
if (IsAlpha(g_prog.u.buf16[0]) && //
|
||||
g_prog.u.buf16[1] == ':' && //
|
||||
g_prog.u.buf16[2] == '/') {
|
||||
// turn c:/... into /c/...
|
||||
u.path16[1] = u.path16[0];
|
||||
u.path16[0] = '/';
|
||||
u.path16[2] = '/';
|
||||
g_prog.u.buf16[1] = g_prog.u.buf16[0];
|
||||
g_prog.u.buf16[0] = '/';
|
||||
g_prog.u.buf16[2] = '/';
|
||||
}
|
||||
tprecode16to8(p, e - p, u.path16);
|
||||
tprecode16to8(g_prog.u.buf, sizeof(g_prog.u.buf), g_prog.u.buf16);
|
||||
return;
|
||||
}
|
||||
|
||||
char c, *q;
|
||||
if (IsMetal()) {
|
||||
if (!memccpy(p, APE_COM_NAME, 0, e - p - 1)) {
|
||||
e[-1] = 0;
|
||||
}
|
||||
return;
|
||||
q = APE_COM_NAME;
|
||||
goto CopyString;
|
||||
}
|
||||
|
||||
// if argv[0] exists then turn it into an absolute path. we also try
|
||||
// adding a .com suffix since the ape auto-appends it when resolving
|
||||
if ((q = __argv[0]) && ((!sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
|
||||
((q = StrCat(u.path, __argv[0], ".com")) &&
|
||||
!sys_faccessat(AT_FDCWD, q, F_OK, 0)))) {
|
||||
if ((q = __argv[0])) {
|
||||
char *p = g_prog.u.buf;
|
||||
char *e = p + sizeof(g_prog.u.buf);
|
||||
if (*q != '/') {
|
||||
if (q[0] == '.' && q[1] == '/') {
|
||||
q += 2;
|
||||
}
|
||||
if (getcwd(p, e - p)) {
|
||||
if (getcwd(p, e - p - 1 - 4)) { // for / and .com
|
||||
while (*p) ++p;
|
||||
*p++ = '/';
|
||||
}
|
||||
}
|
||||
for (i = 0; *q && p + 1 < e; ++p, ++q) {
|
||||
*p = *q;
|
||||
while ((c = *q++)) {
|
||||
if (p + 1 + 4 < e) { // for nul and .com
|
||||
*p++ = c;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
return;
|
||||
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
|
||||
p = WRITE32LE(p, READ32LE(".com"));
|
||||
*p = 0;
|
||||
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
|
||||
}
|
||||
|
||||
// if getenv("_") exists then use that
|
||||
for (ep = __envp; (q = *ep); ++ep) {
|
||||
for (char **ep = __envp; (q = *ep); ++ep) {
|
||||
if (*q++ == '_' && *q++ == '=') {
|
||||
while ((c = *q++)) {
|
||||
if (p + 1 < e) {
|
||||
*p++ = c;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
return;
|
||||
goto CopyString;
|
||||
}
|
||||
}
|
||||
|
||||
// if argv[0] doesn't exist, then fallback to interpreter name
|
||||
if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 ||
|
||||
(rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) {
|
||||
p[rc] = 0;
|
||||
ssize_t got;
|
||||
char *b = g_prog.u.buf;
|
||||
size_t n = sizeof(g_prog.u.buf) - 1;
|
||||
if ((got = sys_readlinkat(AT_FDCWD, "/proc/self/exe", b, n)) > 0 ||
|
||||
(got = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", b, n)) > 0) {
|
||||
b[got] = 0;
|
||||
return;
|
||||
}
|
||||
if (IsFreebsd() || IsNetbsd()) {
|
||||
u.cmd[0] = CTL_KERN;
|
||||
u.cmd[1] = KERN_PROC;
|
||||
int cmd[4];
|
||||
cmd[0] = CTL_KERN;
|
||||
cmd[1] = KERN_PROC;
|
||||
if (IsFreebsd()) {
|
||||
u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD;
|
||||
cmd[2] = KERN_PROC_PATHNAME_FREEBSD;
|
||||
} else {
|
||||
u.cmd[2] = KERN_PROC_PATHNAME_NETBSD;
|
||||
cmd[2] = KERN_PROC_PATHNAME_NETBSD;
|
||||
}
|
||||
u.cmd[3] = -1; // current process
|
||||
n = e - p;
|
||||
if (sys_sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) {
|
||||
cmd[3] = -1; // current process
|
||||
if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise give up and just copy argv[0] into it
|
||||
if (!*p && (q = __argv[0])) {
|
||||
// give up and just copy argv[0] into it
|
||||
if ((q = __argv[0])) {
|
||||
CopyString:
|
||||
char *p = g_prog.u.buf;
|
||||
char *e = p + sizeof(g_prog.u.buf);
|
||||
while ((c = *q++)) {
|
||||
if (p + 1 < e) {
|
||||
*p++ = c;
|
||||
|
@ -155,14 +146,14 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
|||
}
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
// if we don't even have that then empty the string
|
||||
g_prog.u.buf[0] = 0;
|
||||
}
|
||||
|
||||
static void InitProgramExecutableName(void) {
|
||||
int e;
|
||||
e = errno;
|
||||
GetProgramExecutableNameImpl(
|
||||
program_executable_name.buf,
|
||||
program_executable_name.buf + sizeof(program_executable_name.buf));
|
||||
int e = errno;
|
||||
InitProgramExecutableNameImpl();
|
||||
errno = e;
|
||||
}
|
||||
|
||||
|
@ -170,6 +161,6 @@ static void InitProgramExecutableName(void) {
|
|||
* Returns absolute path of program.
|
||||
*/
|
||||
char *GetProgramExecutableName(void) {
|
||||
cosmo_once(&program_executable_name.once, InitProgramExecutableName);
|
||||
return program_executable_name.buf;
|
||||
cosmo_once(&g_prog.once, InitProgramExecutableName);
|
||||
return g_prog.u.buf;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
|
|||
if (!usage) return 0;
|
||||
me = GetCurrentProcess();
|
||||
if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
|
||||
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
|
||||
who == RUSAGE_SELF ? GetCurrentProcess() : GetCurrentThread(),
|
||||
&ftCreation, &ftExit, &ftKernel, &ftUser) ||
|
||||
!GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) ||
|
||||
!GetProcessIoCounters(me, &iocount)) {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
axdx_t sys_gettimeofday_m1(struct timeval *tv, struct timezone *tz, void *wut) {
|
||||
axdx_t ad;
|
||||
struct timespec ts;
|
||||
ad.ax = _sysret(__syslib->clock_gettime(CLOCK_REALTIME, &ts));
|
||||
ad.ax = _sysret(__syslib->__clock_gettime(CLOCK_REALTIME, &ts));
|
||||
ad.dx = 0;
|
||||
if (!ad.ax && tv) {
|
||||
*tv = timespec_totimeval(ts);
|
||||
|
|
|
@ -49,21 +49,15 @@ static gettimeofday_f *__gettimeofday = __gettimeofday_init;
|
|||
*
|
||||
* @param tv points to timeval that receives result if non-NULL
|
||||
* @param tz receives UTC timezone if non-NULL
|
||||
* @error EFAULT if `tv` or `tz` isn't valid memory
|
||||
* @see clock_gettime() for nanosecond precision
|
||||
* @see strftime() for string formatting
|
||||
*/
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz) {
|
||||
int rc;
|
||||
if (IsAsan() && ((tv && !__asan_is_valid(tv, sizeof(*tv))) ||
|
||||
(tz && !__asan_is_valid(tz, sizeof(*tz))))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
rc = __gettimeofday(tv, tz, 0).ax;
|
||||
}
|
||||
int rc = __gettimeofday(tv, tz, 0).ax;
|
||||
#if SYSDEBUG
|
||||
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
|
||||
POLLTRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz,
|
||||
rc);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
|
||||
// asan must be disabled because __proc_worker calls this on win32 stack
|
||||
|
||||
static uint32_t __kmp32(const void *buf, size_t size) {
|
||||
size_t i;
|
||||
uint32_t h;
|
||||
|
@ -31,7 +33,7 @@ static uint32_t __kmp32(const void *buf, size_t size) {
|
|||
return h;
|
||||
}
|
||||
|
||||
textwindows uint32_t __synthesize_uid(void) {
|
||||
textwindows uint32_t sys_getuid_nt(void) {
|
||||
char16_t buf[257];
|
||||
static atomic_uint uid;
|
||||
uint32_t tmp, size = ARRAYLEN(buf);
|
|
@ -47,7 +47,7 @@ uint32_t getuid(void) {
|
|||
} else if (!IsWindows()) {
|
||||
rc = sys_getuid();
|
||||
} else {
|
||||
rc = __synthesize_uid();
|
||||
rc = sys_getuid_nt();
|
||||
}
|
||||
npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getuid", rc);
|
||||
|
@ -72,7 +72,7 @@ uint32_t getgid(void) {
|
|||
} else if (!IsWindows()) {
|
||||
rc = sys_getgid();
|
||||
} else {
|
||||
rc = __synthesize_uid();
|
||||
rc = sys_getuid_nt();
|
||||
}
|
||||
npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getgid", rc);
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
||||
|
||||
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
||||
#define kSigOpRestartable 1
|
||||
#define kSigOpNochld 2
|
||||
#define kSigOpUnmaskable 4
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -27,7 +24,7 @@ void __releasefd(int);
|
|||
int __ensurefds(int);
|
||||
int __ensurefds_unlocked(int);
|
||||
void __printfds(void);
|
||||
uint32_t __synthesize_uid(void);
|
||||
uint32_t sys_getuid_nt(void);
|
||||
|
||||
forceinline int64_t __getfdhandleactual(int fd) {
|
||||
return g_fds.p[fd].handle;
|
||||
|
|
|
@ -18,35 +18,28 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/struct/windowplacement.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
textwindows int _check_interrupts(int sigops) {
|
||||
int e, rc, flags = 0;
|
||||
e = errno;
|
||||
int status;
|
||||
errno_t err;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(rc = _weaken(pthread_testcancel_np)())) {
|
||||
errno = rc;
|
||||
(err = _weaken(pthread_testcancel_np)())) {
|
||||
goto Interrupted;
|
||||
}
|
||||
if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) {
|
||||
if (status == 2 && (sigops & kSigOpRestartable)) {
|
||||
return 0;
|
||||
}
|
||||
err = EINTR;
|
||||
Interrupted:
|
||||
pt->abort_errno = errno = err;
|
||||
return -1;
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
flags = __get_tls()->tib_flags;
|
||||
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||
}
|
||||
if (_weaken(_check_sigalrm)) {
|
||||
_weaken(_check_sigalrm)();
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_flags = flags;
|
||||
}
|
||||
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
|
||||
return eintr();
|
||||
}
|
||||
errno = e;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "libc/nt/struct/ipadapteraddresses.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/struct/ifconf.h"
|
||||
#include "libc/sock/struct/ifreq.h"
|
||||
|
@ -485,8 +486,12 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
|
|||
if (IsLinux()) {
|
||||
return sys_ioctl(fd, SIOCGIFCONF, ifc);
|
||||
}
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
bufMax = 15000; /* conservative guesstimate */
|
||||
b = alloca(bufMax);
|
||||
CheckLargeStackAllocation(b, bufMax);
|
||||
#pragma GCC pop_options
|
||||
memcpy(ifcBsd, &bufMax, 8); /* ifc_len */
|
||||
memcpy(ifcBsd + (IsXnu() ? 4 : 8), &b, 8); /* ifc_buf */
|
||||
if ((rc = sys_ioctl(fd, SIOCGIFCONF, &ifcBsd)) != -1) {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
bool32 isexecutable(const char *path) {
|
||||
struct stat st; /* execve() depends on this */
|
||||
struct stat st;
|
||||
if (fstatat(AT_FDCWD, path, &st, 0)) return 0;
|
||||
return !S_ISDIR(st.st_mode) && !!(st.st_mode & 0111);
|
||||
}
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/th32cs.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processentry32.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static inline int GetConsoleCtrlEvent(int sig) {
|
||||
switch (sig) {
|
||||
case SIGINT:
|
||||
return kNtCtrlCEvent;
|
||||
case SIGQUIT:
|
||||
return kNtCtrlBreakEvent;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int sys_kill_nt(int pid, int sig) {
|
||||
bool32 ok;
|
||||
int64_t h;
|
||||
int event, ntpid;
|
||||
|
||||
// is killing everything except init really worth supporting?
|
||||
if (pid == -1) return einval();
|
||||
|
||||
// XXX: NT doesn't really have process groups. For instance the
|
||||
// CreateProcess() flag for starting a process group actually
|
||||
// just does an "ignore ctrl-c" internally.
|
||||
pid = ABS(pid);
|
||||
|
||||
// If we're targeting current process group then just call raise().
|
||||
if (!pid || pid == getpid()) {
|
||||
if (!sig) return 0; // ability check passes
|
||||
return raise(sig);
|
||||
}
|
||||
|
||||
// GenerateConsoleCtrlEvent() will always signal groups and there's
|
||||
// nothing we can do about it, unless we have a GUI GetMessage loop
|
||||
// and alternatively create a centralized signal daemon like cygwin
|
||||
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
// we're killing with SIGINT or SIGQUIT which are the only two
|
||||
// signals we can really use, since TerminateProcess() makes
|
||||
// everything else effectively a SIGKILL ;_;
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||
} else if (!__isfdopen(pid)) {
|
||||
ntpid = pid; // XXX: suboptimal
|
||||
} else {
|
||||
return esrch();
|
||||
}
|
||||
if (GenerateConsoleCtrlEvent(event, ntpid)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// is this a cosmo pid that was returned by fork?
|
||||
if (__isfdkind(pid, kFdProcess)) {
|
||||
if (!sig) return 0; // ability check passes
|
||||
// since windows can't execve we need to kill the grandchildren
|
||||
// TODO(jart): should we just kill the whole tree too? there's
|
||||
// no obvious way to tell if it's the execve shell
|
||||
struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)};
|
||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||
int64_t hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
|
||||
if (Process32First(hSnap, &pe)) {
|
||||
do {
|
||||
if (pe.th32ParentProcessID == ntpid) {
|
||||
if ((h = OpenProcess(kNtProcessTerminate, false, pe.th32ProcessID))) {
|
||||
TerminateProcess(h, sig);
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
} while (Process32Next(hSnap, &pe));
|
||||
}
|
||||
CloseHandle(hSnap);
|
||||
ok = TerminateProcess(g_fds.p[pid].handle, sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// XXX: Is this a raw new technology pid? Because that's messy.
|
||||
if ((h = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
if (sig) {
|
||||
ok = TerminateProcess(h, sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) {
|
||||
ok = true; // cargo culting other codebases here
|
||||
}
|
||||
}
|
||||
CloseHandle(h);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -20,17 +20,24 @@
|
|||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
|
||||
textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd,
|
||||
const char *newpath) {
|
||||
char16_t newpath16[PATH_MAX];
|
||||
char16_t oldpath16[PATH_MAX];
|
||||
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) != -1 &&
|
||||
__mkntpathat(newdirfd, newpath, 0, newpath16) != -1) {
|
||||
if (CreateHardLink(newpath16, oldpath16, NULL)) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct {
|
||||
char16_t newpath16[PATH_MAX];
|
||||
char16_t oldpath16[PATH_MAX];
|
||||
} M;
|
||||
CheckLargeStackAllocation(&M, sizeof(M));
|
||||
#pragma GCC pop_options
|
||||
if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) != -1 &&
|
||||
__mkntpathat(newdirfd, newpath, 0, M.newpath16) != -1) {
|
||||
if (CreateHardLink(M.newpath16, M.oldpath16, NULL)) {
|
||||
return 0;
|
||||
} else {
|
||||
return __fix_enotdir3(__winerr(), newpath16, oldpath16);
|
||||
return __fix_enotdir3(__winerr(), M.newpath16, M.oldpath16);
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
|
|
|
@ -16,21 +16,63 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
#include "libc/stdckdint.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
|
||||
int64_t res;
|
||||
if (__isfdkind(fd, kFdFile)) {
|
||||
if (GetFileType(g_fds.p[fd].handle) != kNtFileTypePipe) {
|
||||
if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) {
|
||||
return res;
|
||||
} else {
|
||||
static textwindows int64_t GetPosition(struct Fd *f, int whence) {
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
return 0;
|
||||
case SEEK_CUR:
|
||||
return f->pointer;
|
||||
case SEEK_END: {
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (!GetFileInformationByHandle(f->handle, &wst)) {
|
||||
return __winerr();
|
||||
}
|
||||
return (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
||||
}
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) {
|
||||
int64_t pos;
|
||||
if ((pos = GetPosition(f, whence)) != -1) {
|
||||
if (!ckd_add(&pos, pos, offset)) {
|
||||
if (pos >= 0) {
|
||||
return pos;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
} else {
|
||||
return eoverflow();
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
|
||||
if (__isfdkind(fd, kFdFile)) {
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
int filetype = GetFileType(f->handle);
|
||||
if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) {
|
||||
int64_t res;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
if ((res = Seek(f, offset, whence)) != -1) {
|
||||
f->pointer = res;
|
||||
}
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
return res;
|
||||
} else {
|
||||
return espipe();
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ __static_yoink("_init_metalfile");
|
|||
void *__ape_com_base;
|
||||
size_t __ape_com_size = 0;
|
||||
|
||||
textstartup dontasan void InitializeMetalFile(void) {
|
||||
textstartup void InitializeMetalFile(void) {
|
||||
if (IsMetal()) {
|
||||
/*
|
||||
* Copy out a pristine image of the program — before the program might
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
* with random text on success (and not modified on error)
|
||||
* @return pointer to template on success, or NULL w/ errno
|
||||
* @raise EINVAL if template didn't end with XXXXXX
|
||||
* @cancellationpoint
|
||||
*/
|
||||
char *mkdtemp(char *template) {
|
||||
int n;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
|
@ -141,7 +142,12 @@ textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[],
|
|||
bool have_systemroot = false;
|
||||
size_t i, j, k, n, m, bufi = 0;
|
||||
for (n = 0; envp[n];) n++;
|
||||
vars = alloca((n + 1) * sizeof(char *));
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (n + 1) * sizeof(char *);
|
||||
vars = alloca(nbytes);
|
||||
CheckLargeStackAllocation(vars, nbytes);
|
||||
#pragma GCC pop_options
|
||||
for (i = 0; i < n; ++i) {
|
||||
InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/temp.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/temp.h"
|
||||
|
||||
/**
|
||||
* Creates temporary file name and file descriptor, e.g.
|
||||
|
@ -34,6 +34,7 @@
|
|||
* @see mkstemp() if you don't need flags
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
*/
|
||||
int mkostemp(char *template, unsigned flags) {
|
||||
return openatemp(AT_FDCWD, template, 0, flags, 0);
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/temp.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/temp.h"
|
||||
|
||||
/**
|
||||
* Creates temporary file name and file descriptor, e.g.
|
||||
|
@ -35,6 +35,7 @@
|
|||
* @see mkostemp() if you don't need suffix
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
*/
|
||||
int mkostemps(char *template, int suffixlen, unsigned flags) {
|
||||
return openatemp(AT_FDCWD, template, suffixlen, flags, 0);
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/temp.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/temp.h"
|
||||
|
||||
/**
|
||||
* Creates temporary file name and file descriptor, e.g.
|
||||
|
@ -34,6 +34,7 @@
|
|||
* @see mkstemps() if you you need a suffix
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
*/
|
||||
int mkstemp(char *template) {
|
||||
return openatemp(AT_FDCWD, template, 0, 0, 0);
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/temp.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/temp.h"
|
||||
|
||||
/**
|
||||
* Creates temporary file name and file descriptor, e.g.
|
||||
|
@ -36,6 +36,7 @@
|
|||
* @see mkstemp() if you don't need `suffixlen`
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
*/
|
||||
int mkstemps(char *template, int suffixlen) {
|
||||
return openatemp(AT_FDCWD, template, suffixlen, 0, 0);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* @see mkstemps() if you you need a file extension
|
||||
* @see openatemp() for one temp roller to rule them all
|
||||
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
|
||||
* @cancellationpoint
|
||||
*/
|
||||
char *mktemp(char *template) {
|
||||
int fd;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "libc/runtime/pc.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
dontasan int sys_munmap_metal(void *addr, size_t size) {
|
||||
int sys_munmap_metal(void *addr, size_t size) {
|
||||
size_t i;
|
||||
uint64_t *e, paddr;
|
||||
struct mman *mm = __get_mm();
|
||||
|
|
|
@ -46,6 +46,6 @@ int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) {
|
|||
}
|
||||
return rc;
|
||||
#else
|
||||
return _sysret(__syslib->nanosleep(req, rem));
|
||||
return _sysret(__syslib->__nanosleep(req, rem));
|
||||
#endif
|
||||
}
|
||||
|
|
117
libc/calls/now.c
117
libc/calls/now.c
|
@ -1,117 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/clock_gettime.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
// TODO(jart): DELETE
|
||||
|
||||
static clock_gettime_f *__gettime;
|
||||
|
||||
static struct Now {
|
||||
uint64_t k0;
|
||||
long double r0, cpn;
|
||||
} g_now;
|
||||
|
||||
static long double GetTimeSample(void) {
|
||||
uint64_t tick1, tick2;
|
||||
long double time1, time2;
|
||||
sched_yield();
|
||||
time1 = dtime(CLOCK_REALTIME_PRECISE);
|
||||
tick1 = rdtsc();
|
||||
nanosleep(&(struct timespec){0, 1000000}, NULL);
|
||||
time2 = dtime(CLOCK_REALTIME_PRECISE);
|
||||
tick2 = rdtsc();
|
||||
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
|
||||
}
|
||||
|
||||
static long double MeasureNanosPerCycle(void) {
|
||||
int i, n;
|
||||
long double avg, samp;
|
||||
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||
if (IsWindows()) {
|
||||
n = 10;
|
||||
} else {
|
||||
n = 5;
|
||||
}
|
||||
for (avg = 1.0L, i = 1; i < n; ++i) {
|
||||
samp = GetTimeSample();
|
||||
avg += (samp - avg) / i;
|
||||
}
|
||||
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
|
||||
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
|
||||
return avg;
|
||||
}
|
||||
|
||||
void RefreshTime(void) {
|
||||
struct Now now;
|
||||
now.cpn = MeasureNanosPerCycle();
|
||||
now.r0 = dtime(CLOCK_REALTIME_PRECISE);
|
||||
now.k0 = rdtsc();
|
||||
memcpy(&g_now, &now, sizeof(now));
|
||||
}
|
||||
|
||||
static long double nowl_sys(void) {
|
||||
return dtime(CLOCK_REALTIME_PRECISE);
|
||||
}
|
||||
|
||||
static long double nowl_art(void) {
|
||||
uint64_t ticks = rdtsc() - g_now.k0;
|
||||
return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn));
|
||||
}
|
||||
|
||||
static long double nowl_vdso(void) {
|
||||
long double secs;
|
||||
struct timespec tv;
|
||||
unassert(__gettime);
|
||||
__gettime(CLOCK_REALTIME_PRECISE, &tv);
|
||||
secs = tv.tv_nsec;
|
||||
secs *= 1 / 1e9L;
|
||||
secs += tv.tv_sec;
|
||||
return secs;
|
||||
}
|
||||
|
||||
long double nowl_setup(void) {
|
||||
bool isfast;
|
||||
__gettime = __clock_gettime_get(&isfast);
|
||||
if (isfast) {
|
||||
nowl = nowl_vdso;
|
||||
} else if (X86_HAVE(INVTSC)) {
|
||||
RefreshTime();
|
||||
nowl = nowl_art;
|
||||
} else {
|
||||
nowl = nowl_sys;
|
||||
}
|
||||
return nowl();
|
||||
}
|
||||
|
||||
long double (*nowl)(void) = nowl_setup;
|
|
@ -1,84 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
// TODO(jart): DELETE
|
||||
|
||||
static struct Now {
|
||||
bool once;
|
||||
uint64_t k0;
|
||||
long double r0, cpn;
|
||||
} g_now;
|
||||
|
||||
static long double GetTimeSample(void) {
|
||||
uint64_t tick1, tick2;
|
||||
long double time1, time2;
|
||||
sched_yield();
|
||||
time1 = dtime(CLOCK_MONOTONIC);
|
||||
tick1 = rdtsc();
|
||||
nanosleep(&(struct timespec){0, 1000000}, NULL);
|
||||
time2 = dtime(CLOCK_MONOTONIC);
|
||||
tick2 = rdtsc();
|
||||
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
|
||||
}
|
||||
|
||||
static long double MeasureNanosPerCycle(void) {
|
||||
int i, n;
|
||||
long double avg, samp;
|
||||
if (__tls_enabled) __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||
if (IsWindows()) {
|
||||
n = 30;
|
||||
} else {
|
||||
n = 20;
|
||||
}
|
||||
for (avg = 1.0L, i = 1; i < n; ++i) {
|
||||
samp = GetTimeSample();
|
||||
avg += (samp - avg) / i;
|
||||
}
|
||||
if (__tls_enabled) __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
|
||||
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
|
||||
return avg;
|
||||
}
|
||||
|
||||
static void Refresh(void) {
|
||||
struct Now now;
|
||||
now.cpn = MeasureNanosPerCycle();
|
||||
now.r0 = dtime(CLOCK_REALTIME);
|
||||
now.k0 = rdtsc();
|
||||
now.once = true;
|
||||
memcpy(&g_now, &now, sizeof(now));
|
||||
}
|
||||
|
||||
long double ConvertTicksToNanos(double ticks) {
|
||||
if (!g_now.once) Refresh();
|
||||
return ticks * g_now.cpn; /* pico scale */
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
|
||||
|
||||
static textwindows int GetSig(uint32_t dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
return SIGINT;
|
||||
case kNtCtrlBreakEvent:
|
||||
return SIGQUIT;
|
||||
case kNtCtrlCloseEvent:
|
||||
case kNtCtrlLogoffEvent: // only received by services
|
||||
case kNtCtrlShutdownEvent: // only received by services
|
||||
return SIGHUP;
|
||||
default:
|
||||
return SIGSTKFLT;
|
||||
}
|
||||
}
|
||||
|
||||
textwindows bool32 __sig_notify(int sig, int sic) {
|
||||
|
||||
// we're on a stack that's owned by win32. to make matters worse,
|
||||
// win32 spawns a totally new thread just to invoke this handler.
|
||||
if (__sighandrvas[sig] == (uintptr_t)SIG_IGN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we don't have tls, then we can't hijack a safe stack from a
|
||||
// thread so just try our luck punting the signal to the next i/o
|
||||
if (!__tls_enabled) {
|
||||
__sig.pending |= 1ull << (sig - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
|
||||
// before we get asynchronous, let's try to find a thread that is
|
||||
// currently blocked on io which we can interrupt with our signal
|
||||
// this is important, because if we we asynchronously interrupt a
|
||||
// thread that's calling ReadFile() by suspending / resuming then
|
||||
// the io operation will report end of file (why) upon resumation
|
||||
struct Dll *e;
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
|
||||
if (tid <= 0) continue; // -1 means spawning, 0 means terminated
|
||||
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked
|
||||
if (pt->flags & PT_BLOCKED) {
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
__sig.pending |= 1ull << (sig - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// limbo means most of the cosmo runtime is totally broken, which
|
||||
// means we can't call the user signal handler safely. what we'll
|
||||
// do instead is pick a posix thread at random to hijack, pretend
|
||||
// to be that thread, use its stack, and then deliver this signal
|
||||
// asynchronously if it isn't blocked. hopefully it won't longjmp
|
||||
// because once the handler returns, we'll restore the old thread
|
||||
// going asynchronous is heavy but it's the only way to stop code
|
||||
// that does stuff like scientific computing, which are cpu-bound
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
|
||||
if (tid <= 0) continue; // -1 means spawning, 0 means terminated
|
||||
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
_pthread_signal(pt, sig, sic);
|
||||
return true;
|
||||
}
|
||||
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
__msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
|
||||
__sig_notify(GetSig(dwCtrlType), SI_KERNEL);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -1,28 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
.init.start 300,_init_onntconsoleevent
|
||||
testb IsWindows()
|
||||
jz 1f
|
||||
ezlea __onntconsoleevent,cx
|
||||
pushpop 1,%rdx
|
||||
ntcall __imp_SetConsoleCtrlHandler
|
||||
1: .init.end 300,_init_onntconsoleevent,globl,hidden
|
|
@ -161,8 +161,9 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
|
||||
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
|
||||
kNtFileFlagOverlapped)) != -1) {
|
||||
g_fds.p[fd].extra = sys_open_nt_impl(
|
||||
dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode, 0);
|
||||
g_fds.p[fd].extra =
|
||||
sys_open_nt_impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY,
|
||||
mode, kNtFileFlagOverlapped);
|
||||
npassert(g_fds.p[fd].extra != -1);
|
||||
} else {
|
||||
return -1;
|
||||
|
@ -176,8 +177,8 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
static textwindows int sys_open_nt_file(int dirfd, const char *file,
|
||||
uint32_t flags, int32_t mode,
|
||||
size_t fd) {
|
||||
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, 0)) !=
|
||||
-1) {
|
||||
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode,
|
||||
kNtFileFlagOverlapped)) != -1) {
|
||||
g_fds.p[fd].kind = kFdFile;
|
||||
g_fds.p[fd].flags = flags;
|
||||
g_fds.p[fd].mode = mode;
|
||||
|
|
|
@ -86,9 +86,7 @@
|
|||
* @param template is a pathname relative to current directory by default,
|
||||
* that needs to have "XXXXXX" at the end of the string; this memory
|
||||
* must be mutable and should be owned by the calling thread; it will
|
||||
* be modified (only on success) to return the generated filename; it
|
||||
* is recommended that the caller use `kTmpPath` at the beginning of
|
||||
* the generated `template` path and then set `dirfd` to `AT_FDCWD`
|
||||
* be modified (only on success) to return the generated filename
|
||||
* @param suffixlen may be nonzero to permit characters after the "XXXXXX"
|
||||
* @param mode is conventionally 0600, for owner-only non-exec access
|
||||
* @param flags could have O_APPEND, O_CLOEXEC, O_UNLINK, O_SYNC, etc.
|
||||
|
@ -97,6 +95,7 @@
|
|||
* @raise EINVAL if `template` (less the `suffixlen` region) didn't
|
||||
* end with the string "XXXXXXX"
|
||||
* @raise EINVAL if `suffixlen` was negative or too large
|
||||
* @cancellationpoint
|
||||
*/
|
||||
int openatemp(int dirfd, char *template, int suffixlen, int flags, int mode) {
|
||||
flags &= ~O_ACCMODE;
|
||||
|
|
|
@ -16,37 +16,27 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
textwindows int sys_pause_nt(void) {
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
for (;;) {
|
||||
|
||||
if (_check_interrupts(0)) {
|
||||
return -1;
|
||||
int rc;
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR"); // in case we ever figure it out
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(SYSDEBUG) && _POLLTRACE
|
||||
long ms = 0, totoms = 0;
|
||||
ms += __SIG_POLLING_INTERVAL_MS;
|
||||
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
|
||||
totoms += ms, ms = 0;
|
||||
POLLTRACE("... pausing for %'lums...", totoms);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
END_BLOCKING_OPERATION;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1078,81 +1078,81 @@ static privileged int GetTid(void) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static privileged long Write(int fd, const void *p, unsigned long n) {
|
||||
#ifdef __x86_64__
|
||||
long res;
|
||||
asm volatile("syscall"
|
||||
: "=a"(res)
|
||||
: "0"(__NR_linux_write), "D"(2), "S"(p), "d"(n)
|
||||
: "rcx", "r11", "memory");
|
||||
return res;
|
||||
#elif defined(__aarch64__)
|
||||
register long x0 asm("x0") = 2;
|
||||
register long x1 asm("x1") = (long)p;
|
||||
register long x2 asm("x2") = n;
|
||||
register long x8 asm("x8") = __NR_linux_write;
|
||||
asm volatile("svc\t0" : "+r"(x0) : "r"(x1), "r"(x2), "r"(x8) : "memory");
|
||||
return x0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static privileged void Log(const char *s, ...) {
|
||||
va_list va;
|
||||
va_start(va, s);
|
||||
do {
|
||||
#ifdef __x86_64__
|
||||
int res;
|
||||
asm volatile("syscall"
|
||||
: "=a"(res)
|
||||
: "0"(__NR_linux_write), "D"(2), "S"(s), "d"(StrLen(s))
|
||||
: "rcx", "r11", "memory");
|
||||
#elif defined(__aarch64__)
|
||||
register long r0 asm("x0") = 2;
|
||||
register long r1 asm("x1") = (long)s;
|
||||
register long r2 asm("x2") = StrLen(s);
|
||||
register long res_x0 asm("x0");
|
||||
asm volatile("mov\tx8,%1\n\t"
|
||||
"svc\t0"
|
||||
: "=r"(res_x0)
|
||||
: "i"(__NR_linux_write), "r"(r0), "r"(r1), "r"(r2)
|
||||
: "x8", "memory");
|
||||
#endif
|
||||
Write(2, s, StrLen(s));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static privileged int SigAction(int sig, struct sigaction *act,
|
||||
struct sigaction *old) {
|
||||
int res;
|
||||
act->sa_flags |= Sa_Restorer;
|
||||
act->sa_restorer = &__restore_rt;
|
||||
#ifdef __x86_64__
|
||||
int res;
|
||||
asm volatile("mov\t%5,%%r10\n\t"
|
||||
"syscall"
|
||||
: "=a"(res)
|
||||
: "0"(__NR_linux_sigaction), "D"(sig), "S"(act), "d"(old), "g"(8)
|
||||
: "rcx", "r10", "r11", "memory");
|
||||
#elif defined(__aarch64__)
|
||||
register long r0 asm("x0") = (long)sig;
|
||||
register long r1 asm("x1") = (long)act;
|
||||
register long r2 asm("x2") = (long)old;
|
||||
register long r3 asm("x3") = (long)8;
|
||||
register long res_x0 asm("x0");
|
||||
asm volatile("mov\tx8,%1\n\t"
|
||||
"svc\t0"
|
||||
: "=r"(res_x0)
|
||||
: "i"(__NR_linux_sigaction), "r"(r0), "r"(r1), "r"(r2), "r"(r3)
|
||||
: "x8", "memory");
|
||||
res = res_x0;
|
||||
#endif
|
||||
return res;
|
||||
#elif defined(__aarch64__)
|
||||
register int x0 asm("x0") = sig;
|
||||
register void *x1 asm("x1") = act;
|
||||
register void *x2 asm("x2") = old;
|
||||
register int x3 asm("x3") = 8;
|
||||
register int x8 asm("x8") = __NR_linux_sigaction;
|
||||
asm volatile("svc\t0"
|
||||
: "+r"(x0)
|
||||
: "r"(x1), "r"(x2), "r"(x3), "r"(x8)
|
||||
: "memory");
|
||||
return x0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static privileged int SigProcMask(int how, int64_t set, int64_t *old) {
|
||||
int res;
|
||||
#ifdef __x86_64__
|
||||
int res;
|
||||
asm volatile("mov\t%5,%%r10\n\t"
|
||||
"syscall"
|
||||
: "=a"(res)
|
||||
: "0"(__NR_linux_sigprocmask), "D"(how), "S"(&set), "d"(old),
|
||||
"g"(8)
|
||||
: "rcx", "r10", "r11", "memory");
|
||||
#elif defined(__aarch64__)
|
||||
register long r0 asm("x0") = (long)how;
|
||||
register long r1 asm("x1") = (long)set;
|
||||
register long r2 asm("x2") = (long)old;
|
||||
register long r3 asm("x3") = (long)8;
|
||||
register long res_x0 asm("x0");
|
||||
asm volatile("mov\tx8,%1\n\t"
|
||||
"svc\t0"
|
||||
: "=r"(res_x0)
|
||||
: "i"(__NR_linux_sigprocmask), "r"(r0), "r"(r1), "r"(r2), "r"(r3)
|
||||
: "x8", "memory");
|
||||
res = res_x0;
|
||||
#endif
|
||||
return res;
|
||||
#elif defined(__aarch64__)
|
||||
register int x0 asm("x0") = how;
|
||||
register void *x1 asm("x1") = &set;
|
||||
register void *x2 asm("x2") = old;
|
||||
register int x3 asm("x3") = 8;
|
||||
register long x8 asm("x8") = __NR_linux_sigprocmask;
|
||||
asm volatile("svc\t0"
|
||||
: "+r"(x0)
|
||||
: "r"(x1), "r"(x2), "r"(x3), "r"(x8)
|
||||
: "memory");
|
||||
return x0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static privileged void KillThisProcess(void) {
|
||||
|
@ -1205,6 +1205,16 @@ static privileged void KillThisThread(void) {
|
|||
: "0"(__NR_linux_tkill), "D"(GetTid()), "S"(Sigabrt)
|
||||
: "rcx", "r11", "memory");
|
||||
#elif defined(__aarch64__)
|
||||
{
|
||||
register long r0 asm("x0") = (long)GetTid();
|
||||
register long r1 asm("x1") = (long)Sigabrt;
|
||||
register long res_x0 asm("x0");
|
||||
asm volatile("mov\tx8,%1\n\t"
|
||||
"svc\t0"
|
||||
: "=r"(res_x0)
|
||||
: "i"(__NR_linux_tkill), "r"(r0), "r"(r1)
|
||||
: "x8", "memory");
|
||||
}
|
||||
#endif
|
||||
SigProcMask(Sig_Setmask, 0, 0);
|
||||
#ifdef __x86_64__
|
||||
|
@ -1267,6 +1277,7 @@ static privileged void OnSigSys(int sig, siginfo_t *si, void *vctx) {
|
|||
}
|
||||
switch (mode & PLEDGE_PENALTY_MASK) {
|
||||
case PLEDGE_PENALTY_KILL_PROCESS:
|
||||
KillThisThread();
|
||||
KillThisProcess();
|
||||
// fallthrough
|
||||
case PLEDGE_PENALTY_KILL_THREAD:
|
||||
|
@ -2290,10 +2301,13 @@ static privileged void AppendPledge(struct Filter *f, //
|
|||
* @vforksafe
|
||||
*/
|
||||
privileged int sys_pledge_linux(unsigned long ipromises, int mode) {
|
||||
int i, rc = -1;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct Filter f;
|
||||
struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)};
|
||||
CheckLargeStackAllocation(&f, sizeof(f));
|
||||
#pragma GCC pop_options
|
||||
struct sock_filter sf[1] = {BPF_STMT(BPF_RET | BPF_K, 0)};
|
||||
int i, rc = -1;
|
||||
f.n = 0;
|
||||
|
||||
// set up the seccomp filter
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/pledge.internal.h"
|
||||
#include "libc/calls/prctl.internal.h"
|
||||
|
@ -57,15 +58,7 @@
|
|||
* self-imposed security model. That works best when programs perform
|
||||
* permission-hungry operations (e.g. calling GetSymbolTable) towards
|
||||
* the beginning of execution, and then relinquish privilege afterwards
|
||||
* by calling pledge(). Here's an example of where that matters. Your
|
||||
* Cosmopolitan C Library needs to code morph your executable in memory
|
||||
* once you start using threads. But that's only possible to do if you
|
||||
* used the `prot_exec` promise. So the right thing to do here, is to
|
||||
* call __enable_threads() before calling pledge() to force it early.
|
||||
*
|
||||
* __enable_threads();
|
||||
* ShowCrashReports();
|
||||
* pledge("...", 0);
|
||||
* by calling pledge().
|
||||
*
|
||||
* By default exit() is allowed. This is useful for processes that
|
||||
* perform pure computation and interface with the parent via shared
|
||||
|
@ -260,7 +253,9 @@ int pledge(const char *promises, const char *execpromises) {
|
|||
if (IsGenuineBlink()) return enosys();
|
||||
if (IsOpenbsd()) return sys_pledge(0, 0);
|
||||
if (!IsLinux()) return enosys();
|
||||
if (!(rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0))) return 0;
|
||||
rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
|
||||
if (rc == 0 || rc == 2) return 0; // 2 means we're already filtered
|
||||
unassert(rc < 0);
|
||||
errno = -rc;
|
||||
return -1;
|
||||
} else if (!IsTiny() && IsGenuineBlink()) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -42,6 +43,7 @@
|
|||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
|
@ -161,31 +163,31 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
}
|
||||
// if we haven't found any good results yet then here we
|
||||
// compute a small time slice we don't mind sleeping for
|
||||
waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLLING_INTERVAL_MS, *ms);
|
||||
waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLL_INTERVAL_MS, *ms);
|
||||
if (sn) {
|
||||
// we need to poll the socket handles separately because
|
||||
// microsoft certainly loves to challenge us with coding
|
||||
// please note that winsock will fail if we pass zero fd
|
||||
#if _NTTRACE
|
||||
POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms);
|
||||
#endif
|
||||
if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) {
|
||||
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
|
||||
rc = __winsockerr();
|
||||
goto ReturnPath;
|
||||
}
|
||||
*ms -= waitfor;
|
||||
} else {
|
||||
gotsocks = 0;
|
||||
if (!gotinvals && !gotpipes && waitfor) {
|
||||
// if we've only got pipes and none of them are ready
|
||||
// then we'll just explicitly sleep for the time left
|
||||
POLLTRACE("SleepEx(%'d, false) out of %'lu", waitfor, *ms);
|
||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR");
|
||||
} else {
|
||||
*ms -= waitfor;
|
||||
}
|
||||
}
|
||||
if (!gotinvals && !gotsocks && !gotpipes && waitfor) {
|
||||
POLLTRACE("sleeping for %'d out of %'lu ms", waitfor, *ms);
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, waitfor);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
goto ReturnPath;
|
||||
}
|
||||
*ms -= waitfor; // todo: make more resilient
|
||||
}
|
||||
// we gave all the sockets and all the named pipes a shot
|
||||
// if we found anything at all then it's time to end work
|
||||
|
|
|
@ -50,7 +50,6 @@ void __printfds(void) {
|
|||
for (i = 0; i < g_fds.n; ++i) {
|
||||
if (!g_fds.p[i].kind) continue;
|
||||
kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind));
|
||||
if (g_fds.p[i].zombie) kprintf(" zombie");
|
||||
if (g_fds.p[i].flags) kprintf(" flags=%#x", g_fds.p[i].flags);
|
||||
if (g_fds.p[i].mode) kprintf(" mode=%#o", g_fds.p[i].mode);
|
||||
if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle);
|
||||
|
|
|
@ -21,52 +21,33 @@
|
|||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/xnu.internal.h"
|
||||
|
||||
static dontubsan void RaiseSigFpe(void) {
|
||||
volatile int x = 0;
|
||||
x = 1 / x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends signal to self.
|
||||
*
|
||||
* This is basically the same as:
|
||||
*
|
||||
* tkill(gettid(), sig);
|
||||
* pthread_kill(pthread_self(), sig);
|
||||
*
|
||||
* Note `SIG_DFL` still results in process death for most signals.
|
||||
*
|
||||
* This function is not entirely equivalent to kill() or tkill(). For
|
||||
* example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that
|
||||
* helps us support Windows. So if the raised signal has a signal
|
||||
* handler, then the reported `si_code` might not be `SI_TKILL`.
|
||||
*
|
||||
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
|
||||
* @return 0 if signal was delivered and returned, or -1 w/ errno
|
||||
* @return 0 on success, or nonzero on failure
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int raise(int sig) {
|
||||
int rc;
|
||||
STRACE("raise(%G) → ...", sig);
|
||||
if (sig == SIGTRAP) {
|
||||
DebugBreak();
|
||||
if (IsXnuSilicon()) {
|
||||
rc = __syslib->__raise(sig);
|
||||
} else if (IsWindows()) {
|
||||
__sig_raise(sig, SI_TKILL);
|
||||
rc = 0;
|
||||
#ifndef __aarch64__
|
||||
} else if (sig == SIGFPE) {
|
||||
// TODO(jart): Why doesn't AARCH64 raise SIGFPE?
|
||||
RaiseSigFpe();
|
||||
rc = 0;
|
||||
#endif
|
||||
} else {
|
||||
rc = tkill(gettid(), sig);
|
||||
rc = sys_tkill(gettid(), sig, 0);
|
||||
}
|
||||
STRACE("...raise(%G) → %d% m", sig, rc);
|
||||
STRACE("raise(%G) → %d% m", sig, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
@ -49,6 +47,8 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
__static_yoink("WinMainStdin");
|
||||
|
||||
|
@ -56,12 +56,6 @@ __static_yoink("WinMainStdin");
|
|||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static textwindows void sys_read_nt_abort(int64_t handle,
|
||||
struct NtOverlapped *overlapped) {
|
||||
unassert(CancelIoEx(handle, overlapped) ||
|
||||
GetLastError() == kNtErrorNotFound);
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
||||
int64_t offset) {
|
||||
|
||||
|
@ -69,20 +63,37 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
|||
bool32 ok;
|
||||
struct Fd *f;
|
||||
uint32_t got;
|
||||
int filetype;
|
||||
int64_t handle;
|
||||
uint32_t remain;
|
||||
char *targetdata;
|
||||
uint32_t targetsize;
|
||||
bool is_console_input;
|
||||
int abort_errno = EAGAIN;
|
||||
struct PosixThread *pt;
|
||||
|
||||
f = g_fds.p + fd;
|
||||
handle = __resolve_stdin_handle(f->handle);
|
||||
pt = _pthread_self();
|
||||
|
||||
bool pwriting = offset != -1;
|
||||
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
||||
bool nonblock = !!(f->flags & O_NONBLOCK);
|
||||
pt->abort_errno = EAGAIN;
|
||||
|
||||
if (pwriting && !seekable) {
|
||||
return espipe();
|
||||
}
|
||||
if (!pwriting) {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
uint32_t dwConsoleMode;
|
||||
bool is_console_input =
|
||||
g_fds.stdin.handle
|
||||
? f->handle == g_fds.stdin.handle
|
||||
: !seekable && (f->kind == kFdConsole ||
|
||||
GetConsoleMode(handle, &dwConsoleMode));
|
||||
|
||||
StartOver:
|
||||
size = MIN(size, 0x7ffff000);
|
||||
handle = __resolve_stdin_handle(f->handle);
|
||||
filetype = GetFileType(handle);
|
||||
is_console_input = g_fds.stdin.handle ? f->handle == g_fds.stdin.handle
|
||||
: f->handle == g_fds.p[0].handle;
|
||||
|
||||
// the caller might be reading a single byte at a time. but we need to
|
||||
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
|
||||
|
@ -103,78 +114,61 @@ StartOver:
|
|||
targetsize = size;
|
||||
}
|
||||
|
||||
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
|
||||
struct NtOverlapped overlap = {0};
|
||||
if (offset != -1) {
|
||||
// pread() and pwrite() should not be called on a pipe or tty
|
||||
return espipe();
|
||||
if (!pwriting && seekable) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
offset = f->pointer;
|
||||
}
|
||||
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
|
||||
.Pointer = offset};
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
if (!nonblock) {
|
||||
pt->ioverlap = &overlap;
|
||||
pt->iohandle = handle;
|
||||
}
|
||||
if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) {
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
// the i/o operation is in flight; blocking is unavoidable
|
||||
// if we're in a non-blocking mode, then immediately abort
|
||||
// if an interrupt is pending then we abort before waiting
|
||||
// otherwise wait for i/o periodically checking interrupts
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
sys_read_nt_abort(handle, &overlap);
|
||||
} else if (_check_interrupts(kSigOpRestartable)) {
|
||||
Interrupted:
|
||||
abort_errno = errno;
|
||||
sys_read_nt_abort(handle, &overlap);
|
||||
} else {
|
||||
for (;;) {
|
||||
uint32_t i;
|
||||
if (g_fds.stdin.inisem) {
|
||||
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
|
||||
}
|
||||
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
|
||||
if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(kSigOpRestartable)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
} else {
|
||||
npassert(!i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
if (ok) {
|
||||
// overlapped is allocated on stack, so it's important we wait
|
||||
// for windows to acknowledge that it's done using that memory
|
||||
ok = GetOverlappedResult(handle, &overlap, &got, true);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
kprintf("you complete me\n");
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
__imp_CloseHandle(overlap.hEvent);
|
||||
if (nonblock) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
} else if (_check_interrupts(kSigOpRestartable)) {
|
||||
Interrupted:
|
||||
pt->abort_errno = errno;
|
||||
CancelIoEx(handle, &overlap);
|
||||
} else {
|
||||
ok = false;
|
||||
for (;;) {
|
||||
uint32_t i;
|
||||
if (g_fds.stdin.inisem) {
|
||||
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
|
||||
}
|
||||
i = WaitForSingleObject(overlap.hEvent, __SIG_IO_INTERVAL_MS);
|
||||
if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(kSigOpRestartable)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (offset == -1) {
|
||||
// perform simple blocking file read
|
||||
ok = ReadFile(handle, targetdata, targetsize, &got, 0);
|
||||
} else {
|
||||
// perform pread()-style file read at particular file offset
|
||||
int64_t position;
|
||||
// save file pointer which windows clobbers, even for overlapped i/o
|
||||
if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) {
|
||||
return __winerr(); // f probably isn't seekable?
|
||||
pt->ioverlap = 0;
|
||||
pt->iohandle = 0;
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
// overlapped is allocated on stack, so it's important we wait
|
||||
// for windows to acknowledge that it's done using that memory
|
||||
ok = GetOverlappedResult(handle, &overlap, &got, nonblock);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
goto BlockingOperation;
|
||||
}
|
||||
struct NtOverlapped overlap = {0};
|
||||
overlap.Pointer = (void *)(uintptr_t)offset;
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
|
||||
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
|
||||
// restore file pointer which windows clobbers, even on error
|
||||
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
|
||||
}
|
||||
__imp_CloseHandle(overlap.hEvent); // __imp_ to avoid log noise
|
||||
|
||||
if (!pwriting && seekable) {
|
||||
if (ok) f->pointer = offset + got;
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
|
@ -210,7 +204,7 @@ StartOver:
|
|||
case kNtErrorAccessDenied: // read doesn't return EACCESS
|
||||
return ebadf(); //
|
||||
case kNtErrorOperationAborted:
|
||||
errno = abort_errno;
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
default:
|
||||
return __winerr();
|
||||
|
@ -225,9 +219,16 @@ textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen,
|
|||
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
|
||||
if (iovlen) {
|
||||
for (total = i = 0; i < iovlen; ++i) {
|
||||
// TODO(jart): disable cancelations after first iteration
|
||||
if (!iov[i].iov_len) continue;
|
||||
rc = sys_read_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset);
|
||||
if (rc == -1) return -1;
|
||||
if (rc == -1) {
|
||||
if (total && errno != ECANCELED) {
|
||||
return total;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
total += rc;
|
||||
if (opt_offset != -1) opt_offset += rc;
|
||||
if (rc < iov[i].iov_len) break;
|
||||
|
|
|
@ -46,9 +46,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
|||
|
||||
int64_t h;
|
||||
ssize_t rc;
|
||||
uint32_t mem = 16384;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
uint32_t mem = 6000;
|
||||
volatile char *memory = alloca(mem);
|
||||
CheckLargeStackAllocation((char *)memory, mem);
|
||||
#pragma GCC pop_options
|
||||
struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory;
|
||||
if ((h = CreateFile(path16, kNtFileReadAttributes,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -44,24 +45,30 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd,
|
|||
const char *newpath) {
|
||||
|
||||
// translate unix to windows paths
|
||||
char16_t oldpath16[PATH_MAX];
|
||||
char16_t newpath16[PATH_MAX];
|
||||
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 ||
|
||||
__mkntpathat(newdirfd, newpath, 0, newpath16) == -1) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct {
|
||||
char16_t oldpath16[PATH_MAX];
|
||||
char16_t newpath16[PATH_MAX];
|
||||
} M;
|
||||
CheckLargeStackAllocation(&M, sizeof(M));
|
||||
#pragma GCC pop_options
|
||||
if (__mkntpathat(olddirfd, oldpath, 0, M.oldpath16) == -1 ||
|
||||
__mkntpathat(newdirfd, newpath, 0, M.newpath16) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// strip trailing slash
|
||||
// win32 will einval otherwise
|
||||
// normally this is handled by __fix_enotdir()
|
||||
bool old_must_be_dir = StripTrailingSlash(oldpath16);
|
||||
bool new_must_be_dir = StripTrailingSlash(newpath16);
|
||||
bool old_must_be_dir = StripTrailingSlash(M.oldpath16);
|
||||
bool new_must_be_dir = StripTrailingSlash(M.newpath16);
|
||||
|
||||
// test for some known error conditions ahead of time
|
||||
// the enotdir check can't be done reactively
|
||||
// ideally we should resolve symlinks first
|
||||
uint32_t oldattr = __imp_GetFileAttributesW(oldpath16);
|
||||
uint32_t newattr = __imp_GetFileAttributesW(newpath16);
|
||||
uint32_t oldattr = __imp_GetFileAttributesW(M.oldpath16);
|
||||
uint32_t newattr = __imp_GetFileAttributesW(M.newpath16);
|
||||
if ((old_must_be_dir && oldattr != -1u &&
|
||||
!(oldattr & kNtFileAttributeDirectory)) ||
|
||||
(new_must_be_dir && newattr != -1u &&
|
||||
|
@ -78,16 +85,16 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd,
|
|||
} else if ((oldattr & kNtFileAttributeDirectory) &&
|
||||
(newattr & kNtFileAttributeDirectory)) {
|
||||
// both old and new are directories
|
||||
if (!__imp_RemoveDirectoryW(newpath16) &&
|
||||
if (!__imp_RemoveDirectoryW(M.newpath16) &&
|
||||
GetLastError() == kNtErrorDirNotEmpty) {
|
||||
return enotempty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) {
|
||||
if (MoveFileEx(M.oldpath16, M.newpath16, kNtMovefileReplaceExisting)) {
|
||||
return 0;
|
||||
} else {
|
||||
return __fix_enotdir3(-1, oldpath16, newpath16);
|
||||
return __fix_enotdir3(-1, M.oldpath16, M.newpath16);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/struct/metatermios.internal.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -60,7 +61,9 @@ void __restore_tty(void) {
|
|||
int e;
|
||||
if (__isrestorable && !__isworker && !__nocolor) {
|
||||
e = errno;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
sys_write(0, ANSI_RESTORE, __strlen(ANSI_RESTORE));
|
||||
END_CANCELLATION_POINT;
|
||||
tcsetattr(0, TCSAFLUSH, &__oldtermios);
|
||||
errno = e;
|
||||
}
|
||||
|
|
475
libc/calls/sig.c
Normal file
475
libc/calls/sig.c
Normal file
|
@ -0,0 +1,475 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/enum/exceptionhandleractions.h"
|
||||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct SignalFrame {
|
||||
struct PosixThread *pt;
|
||||
struct NtContext *nc;
|
||||
unsigned rva;
|
||||
unsigned flags;
|
||||
siginfo_t si;
|
||||
};
|
||||
|
||||
struct ContextFrame {
|
||||
struct SignalFrame sf;
|
||||
struct NtContext nc;
|
||||
};
|
||||
|
||||
void __stack_call(int, siginfo_t *, void *, long,
|
||||
void (*)(int, siginfo_t *, void *), void *);
|
||||
|
||||
static textwindows bool __sig_ignored_by_default(int sig) {
|
||||
return sig == SIGURG || //
|
||||
sig == SIGCONT || //
|
||||
sig == SIGCHLD || //
|
||||
sig == SIGWINCH;
|
||||
}
|
||||
|
||||
textwindows bool __sig_ignored(int sig) {
|
||||
return __sighandrvas[sig] == (intptr_t)SIG_IGN ||
|
||||
(__sighandrvas[sig] == (intptr_t)SIG_DFL &&
|
||||
__sig_ignored_by_default(sig));
|
||||
}
|
||||
|
||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
return (flags & SA_ONSTACK) && //
|
||||
tib->tib_sigstack_size && //
|
||||
!(tib->tib_sigstack_flags & (SS_DISABLE | SS_ONSTACK));
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_terminate(int sig) {
|
||||
TerminateThisProcess(sig);
|
||||
}
|
||||
|
||||
static textwindows bool __sig_start(struct PosixThread *pt, int sig,
|
||||
unsigned *rva, unsigned *flags) {
|
||||
*rva = __sighandrvas[sig];
|
||||
*flags = __sighandflags[sig];
|
||||
if (*rva == (intptr_t)SIG_IGN ||
|
||||
(*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return false;
|
||||
}
|
||||
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) {
|
||||
STRACE("tid %d masked %G delivering to tib_sigpending", _pthread_tid(pt),
|
||||
sig);
|
||||
pt->tib->tib_sigpending |= 1ull << (sig - 1);
|
||||
return false;
|
||||
}
|
||||
if (*rva == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
if (*flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx,
|
||||
unsigned rva, unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
sigaction_f handler;
|
||||
handler = (sigaction_f)(__executable_start + rva);
|
||||
++__sig.count;
|
||||
if (__sig_should_use_altstack(flags, tib)) {
|
||||
tib->tib_sigstack_flags |= SS_ONSTACK;
|
||||
__stack_call(sig, si, ctx, 0, handler,
|
||||
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
||||
tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
||||
} else {
|
||||
handler(sig, si, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int __sig_raise(int sig, int sic) {
|
||||
unsigned rva, flags;
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||
ucontext_t ctx = {.uc_sigmask.__bits[0] = tib->tib_sigmask};
|
||||
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
struct NtContext nc;
|
||||
nc.ContextFlags = kNtContextAll;
|
||||
GetThreadContext(GetCurrentThread(), &nc);
|
||||
_ntcontext2linux(&ctx, &nc);
|
||||
STRACE("raising %G", sig);
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
__sig_call(sig, &si, &ctx, rva, flags, tib);
|
||||
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
return (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
|
||||
atomic_int *futex;
|
||||
if (_weaken(WakeByAddressSingle) &&
|
||||
(futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) {
|
||||
_weaken(WakeByAddressSingle)(futex);
|
||||
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
|
||||
pt->abort_errno = EINTR;
|
||||
if (!CancelIoEx(pt->iohandle, pt->ioverlap)) {
|
||||
int err = GetLastError();
|
||||
if (err != kNtErrorNotFound) {
|
||||
STRACE("CancelIoEx() failed w/ %d", err);
|
||||
}
|
||||
}
|
||||
} else if (pt->pt_flags & PT_INSEMAPHORE) {
|
||||
pt->abort_errno = EINTR;
|
||||
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
|
||||
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_panic(const char *msg) {
|
||||
#ifndef TINY
|
||||
char s[128], *p = s;
|
||||
p = stpcpy(p, "sig panic: ");
|
||||
p = stpcpy(p, msg);
|
||||
p = stpcpy(p, " failed w/ ");
|
||||
p = FormatInt32(p, GetLastError());
|
||||
*p++ = '\n';
|
||||
WriteFile(GetStdHandle(kNtStdErrorHandle), s, p - s, 0, 0);
|
||||
#endif
|
||||
TerminateThisProcess(SIGVTALRM);
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
ucontext_t ctx = {0};
|
||||
sigaction_f handler = (sigaction_f)(__executable_start + sf->rva);
|
||||
STRACE("__sig_tramp(%G, %t)", sf->si.si_signo, handler);
|
||||
_ntcontext2linux(&ctx, sf->nc);
|
||||
ctx.uc_sigmask.__bits[0] = sf->pt->tib->tib_sigmask;
|
||||
if (!(sf->flags & SA_NODEFER)) {
|
||||
sf->pt->tib->tib_sigmask |= 1ull << (sf->si.si_signo - 1);
|
||||
}
|
||||
++__sig.count;
|
||||
handler(sf->si.si_signo, &sf->si, &ctx);
|
||||
sf->pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
_ntlinux2context(sf->nc, &ctx);
|
||||
SetThreadContext(GetCurrentThread(), sf->nc);
|
||||
__sig_panic("SetThreadContext(GetCurrentThread)");
|
||||
}
|
||||
|
||||
static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
uintptr_t th;
|
||||
unsigned rva, flags;
|
||||
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
|
||||
th = _pthread_syshand(pt);
|
||||
uint32_t old_suspend_count;
|
||||
old_suspend_count = SuspendThread(th);
|
||||
if (old_suspend_count == -1u) {
|
||||
STRACE("SuspendThread failed w/ %d", GetLastError());
|
||||
return ESRCH;
|
||||
}
|
||||
if (old_suspend_count) {
|
||||
STRACE("kill contention of %u on tid %d", old_suspend_count,
|
||||
_pthread_tid(pt));
|
||||
pt->tib->tib_sigpending |= 1ull << (sig - 1);
|
||||
return 0;
|
||||
}
|
||||
struct NtContext nc;
|
||||
nc.ContextFlags = kNtContextAll;
|
||||
if (!GetThreadContext(th, &nc)) {
|
||||
STRACE("GetThreadContext failed w/ %d", GetLastError());
|
||||
return ESRCH;
|
||||
}
|
||||
uintptr_t sp;
|
||||
if (__sig_should_use_altstack(flags, pt->tib)) {
|
||||
pt->tib->tib_sigstack_flags |= SS_ONSTACK;
|
||||
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
|
||||
pt->tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
||||
} else {
|
||||
sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16;
|
||||
}
|
||||
struct ContextFrame *cf = (struct ContextFrame *)sp;
|
||||
bzero(&cf->sf.si, sizeof(cf->sf.si));
|
||||
memcpy(&cf->nc, &nc, sizeof(nc));
|
||||
cf->sf.pt = pt;
|
||||
cf->sf.rva = rva;
|
||||
cf->sf.nc = &cf->nc;
|
||||
cf->sf.flags = flags;
|
||||
cf->sf.si.si_code = sic;
|
||||
cf->sf.si.si_signo = sig;
|
||||
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip;
|
||||
nc.Rip = (intptr_t)__sig_tramp;
|
||||
nc.Rdi = (intptr_t)&cf->sf;
|
||||
nc.Rsp = sp;
|
||||
if (!SetThreadContext(th, &nc)) {
|
||||
STRACE("SetThreadContext failed w/ %d", GetLastError());
|
||||
return ESRCH;
|
||||
}
|
||||
ResumeThread(th); // doesn't actually resume if doing blocking i/o
|
||||
__sig_cancel(pt, flags); // we can wake it up immediately by canceling it
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
BLOCK_CANCELLATIONS;
|
||||
rc = __sig_killer(pt, sig, sic);
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows void __sig_generate(int sig, int sic) {
|
||||
struct Dll *e;
|
||||
struct PosixThread *pt, *mark = 0;
|
||||
if (__sig_ignored(sig)) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return;
|
||||
}
|
||||
if (__sighandrvas[sig] == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
pt = POSIXTHREAD_CONTAINER(e);
|
||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) <
|
||||
kPosixThreadTerminated &&
|
||||
!(pt->tib->tib_sigmask & (1ull << (sig - 1)))) {
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
if (mark) {
|
||||
__sig_kill(mark, sig, sic);
|
||||
} else {
|
||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
||||
__sig.pending |= sig;
|
||||
}
|
||||
}
|
||||
|
||||
static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
||||
switch (ep->ExceptionRecord->ExceptionCode) {
|
||||
case kNtSignalBreakpoint:
|
||||
*code = TRAP_BRKPT;
|
||||
return SIGTRAP;
|
||||
case kNtSignalIllegalInstruction:
|
||||
*code = ILL_ILLOPC;
|
||||
return SIGILL;
|
||||
case kNtSignalPrivInstruction:
|
||||
*code = ILL_PRVOPC;
|
||||
return SIGILL;
|
||||
case kNtSignalGuardPage:
|
||||
case kNtSignalInPageError:
|
||||
case kNtStatusStackOverflow:
|
||||
*code = SEGV_MAPERR;
|
||||
return SIGSEGV;
|
||||
case kNtSignalAccessViolation:
|
||||
*code = SEGV_ACCERR;
|
||||
return SIGSEGV;
|
||||
case kNtSignalInvalidHandle:
|
||||
case kNtSignalInvalidParameter:
|
||||
case kNtSignalAssertionFailure:
|
||||
*code = SI_USER;
|
||||
return SIGABRT;
|
||||
case kNtStatusIntegerOverflow:
|
||||
*code = FPE_INTOVF;
|
||||
return SIGFPE;
|
||||
case kNtSignalFltDivideByZero:
|
||||
*code = FPE_FLTDIV;
|
||||
return SIGFPE;
|
||||
case kNtSignalFltOverflow:
|
||||
*code = FPE_FLTOVF;
|
||||
return SIGFPE;
|
||||
case kNtSignalFltUnderflow:
|
||||
*code = FPE_FLTUND;
|
||||
return SIGFPE;
|
||||
case kNtSignalFltInexactResult:
|
||||
*code = FPE_FLTRES;
|
||||
return SIGFPE;
|
||||
case kNtSignalFltDenormalOperand:
|
||||
case kNtSignalFltInvalidOperation:
|
||||
case kNtSignalFltStackCheck:
|
||||
case kNtSignalIntegerDivideByZero:
|
||||
case kNtSignalFloatMultipleFaults:
|
||||
case kNtSignalFloatMultipleTraps:
|
||||
*code = FPE_FLTINV;
|
||||
return SIGFPE;
|
||||
case kNtSignalDllNotFound:
|
||||
case kNtSignalOrdinalNotFound:
|
||||
case kNtSignalEntrypointNotFound:
|
||||
case kNtSignalDllInitFailed:
|
||||
*code = SI_KERNEL;
|
||||
return SIGSYS;
|
||||
default:
|
||||
*code = ep->ExceptionRecord->ExceptionCode;
|
||||
return SIGSEGV;
|
||||
}
|
||||
}
|
||||
|
||||
// abashed the devil stood, and felt how awful goodness is
|
||||
__msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
||||
int code, sig = __sig_crash_sig(ep, &code);
|
||||
STRACE("win32 vectored exception 0x%08Xu raising %G "
|
||||
"cosmoaddr2line %s %lx %s",
|
||||
ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name,
|
||||
ep->ContextRecord->Rip,
|
||||
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
||||
if (sig == SIGTRAP) {
|
||||
ep->ContextRecord->Rip++;
|
||||
if (__sig_ignored(sig)) {
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
siginfo_t si = {.si_signo = sig,
|
||||
.si_code = code,
|
||||
.si_addr = ep->ExceptionRecord->ExceptionAddress};
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
unsigned flags = __sighandflags[sig];
|
||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
|
||||
#ifndef TINY
|
||||
intptr_t hStderr;
|
||||
char sigbuf[21], s[128], *p;
|
||||
hStderr = GetStdHandle(kNtStdErrorHandle);
|
||||
p = stpcpy(s, "Terminating on uncaught ");
|
||||
p = stpcpy(p, strsignal_r(sig, sigbuf));
|
||||
p = stpcpy(p, ". Pass --strace and/or ShowCrashReports() for details.\n");
|
||||
WriteFile(hStderr, s, p - s, 0, 0);
|
||||
#endif
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
ucontext_t ctx = {0};
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
ctx.uc_sigmask.__bits[0] = pt->tib->tib_sigmask;
|
||||
__sig_call(sig, &si, &ctx, rva, flags, pt->tib);
|
||||
pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
_ntlinux2context(ep->ContextRecord, &ctx);
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
|
||||
static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
return SIGINT;
|
||||
case kNtCtrlBreakEvent:
|
||||
return SIGQUIT;
|
||||
case kNtCtrlCloseEvent:
|
||||
case kNtCtrlLogoffEvent: // only received by services
|
||||
case kNtCtrlShutdownEvent: // only received by services
|
||||
return SIGHUP;
|
||||
default:
|
||||
return SIGSTKFLT;
|
||||
}
|
||||
}
|
||||
|
||||
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib,
|
||||
const char *thing, int id) {
|
||||
bool handler_was_called = false;
|
||||
uint64_t pending, masked, deliverable;
|
||||
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
||||
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
|
||||
deliverable = pending & ~masked;
|
||||
POLLTRACE("%s %d blocks %d sigs w/ %d pending and %d deliverable", thing, id,
|
||||
popcnt(masked), popcnt(pending), popcnt(deliverable));
|
||||
if (deliverable) {
|
||||
for (int sig = 1; sig <= 64; ++sig) {
|
||||
if ((deliverable & (1ull << (sig - 1))) &&
|
||||
atomic_fetch_and(sigs, ~(1ull << (sig - 1))) & (1ull << (sig - 1))) {
|
||||
handler_was_called |= __sig_raise(sig, SI_KERNEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
// returns 0 if no signal handlers were called, otherwise a bitmask
|
||||
// consisting of `1` which means a signal handler was invoked which
|
||||
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
|
||||
// handlers were called (or `3` if both were the case).
|
||||
textwindows int __sig_check(void) {
|
||||
bool handler_was_called = false;
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
handler_was_called |=
|
||||
__sig_checkem(&tib->tib_sigpending, tib, "tid", tib->tib_tid);
|
||||
handler_was_called |= __sig_checkem(&__sig.pending, tib, "pid", getpid());
|
||||
POLLTRACE("__sig_check() → %hhhd", handler_was_called);
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
textstartup void __sig_init(void) {
|
||||
if (!IsWindows()) return;
|
||||
AddVectoredExceptionHandler(true, (void *)__sig_crash);
|
||||
SetConsoleCtrlHandler((void *)__sig_console, true);
|
||||
}
|
||||
|
||||
const void *const __sig_ctor[] initarray = {__sig_init};
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -1,39 +1,34 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
#define __SIG_QUEUE_LENGTH 32
|
||||
#define __SIG_POLLING_INTERVAL_MS 20
|
||||
#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */
|
||||
#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */
|
||||
#define __SIG_PROC_INTERVAL_MS 1000 /* process waiting also pretty good */
|
||||
#define __SIG_IO_INTERVAL_MS 1000 /* read/write cancel/notify is good */
|
||||
#define __SIG_POLL_INTERVAL_MS 20 /* poll on windows is dumpster fire */
|
||||
#define __SIG_LOGGING_INTERVAL_MS 1700
|
||||
#define __SIG_QUEUE_LENGTH 32
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct Delivery {
|
||||
int ops;
|
||||
int sig;
|
||||
int sic;
|
||||
struct NtContext *nc;
|
||||
};
|
||||
|
||||
struct Signals {
|
||||
uint64_t mask;
|
||||
uint64_t pending;
|
||||
uint64_t count;
|
||||
_Atomic(uint64_t) pending;
|
||||
_Atomic(uint64_t) count;
|
||||
};
|
||||
|
||||
extern struct Signals __sig;
|
||||
|
||||
bool __sig_check(int);
|
||||
bool __sig_is_core(int);
|
||||
bool __sig_is_ignored(int);
|
||||
bool __sig_handle(int, int, int, ucontext_t *);
|
||||
bool __sig_ignored(int);
|
||||
int __sig_check(void);
|
||||
int __sig_kill(struct PosixThread *, int, int);
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||
bool __sig_deliver(int, int, int, ucontext_t *);
|
||||
int __sig_tramp(struct Delivery *);
|
||||
bool32 __sig_notify(int, int);
|
||||
int __sig_raise(int, int);
|
||||
void __sig_cancel(struct PosixThread *, unsigned);
|
||||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -49,13 +49,8 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
__static_yoink("strsignal"); // for kprintf()
|
||||
#endif
|
||||
|
||||
#if SupportsWindows()
|
||||
__static_yoink("_init_onntconsoleevent");
|
||||
__static_yoink("_init_wincrash");
|
||||
__static_yoink("__sig_ctor");
|
||||
#endif
|
||||
|
||||
#define SA_RESTORER 0x04000000
|
||||
|
@ -250,23 +245,21 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
} else {
|
||||
if (oldact) {
|
||||
bzero(oldact, sizeof(*oldact));
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
if (rc != -1 && !__vforked) {
|
||||
if (oldact) {
|
||||
oldrva = __sighandrvas[sig];
|
||||
oldact->sa_flags = __sighandflags[sig];
|
||||
oldact->sa_sigaction =
|
||||
(sigaction_f)(oldrva < kSigactionMinRva
|
||||
? oldrva
|
||||
: (intptr_t)&__executable_start + oldrva);
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
if (rc != -1 && !__vforked) {
|
||||
if (act) {
|
||||
__sighandrvas[sig] = rva;
|
||||
__sighandflags[sig] = act->sa_flags;
|
||||
if (IsWindows()) {
|
||||
if (rva == (intptr_t)SIG_IGN ||
|
||||
(rva == (intptr_t)SIG_DFL && __sig_is_ignored(sig))) {
|
||||
if (__sig_ignored(sig)) {
|
||||
__sig.pending &= ~(1ull << (sig - 1));
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_sigpending &= ~(1ull << (sig - 1));
|
||||
|
@ -281,7 +274,9 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
/**
|
||||
* Installs handler for kernel interrupt to thread, e.g.:
|
||||
*
|
||||
* void GotCtrlC(int sig, siginfo_t *si, void *ctx);
|
||||
* void GotCtrlC(int sig, siginfo_t *si, void *arg) {
|
||||
* ucontext_t *ctx = arg;
|
||||
* }
|
||||
* struct sigaction sa = {.sa_sigaction = GotCtrlC,
|
||||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
|
|
|
@ -24,8 +24,10 @@
|
|||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
static void sigaltstack2bsd(struct sigaltstack_bsd *bsd,
|
||||
const struct sigaltstack *linux) {
|
||||
|
@ -53,14 +55,63 @@ static void sigaltstack2linux(struct sigaltstack *linux,
|
|||
linux->ss_size = size;
|
||||
}
|
||||
|
||||
static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu,
|
||||
struct sigaltstack *old) {
|
||||
struct CosmoTib *tib;
|
||||
tib = __get_tls();
|
||||
if (old) {
|
||||
old->ss_sp = tib->tib_sigstack_addr;
|
||||
old->ss_size = tib->tib_sigstack_size;
|
||||
old->ss_flags = tib->tib_sigstack_flags;
|
||||
}
|
||||
if (neu) {
|
||||
tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16);
|
||||
tib->tib_sigstack_size = ROUNDDOWN(neu->ss_size, 16);
|
||||
tib->tib_sigstack_flags &= SS_ONSTACK;
|
||||
tib->tib_sigstack_flags |= neu->ss_flags & SS_DISABLE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sigaltstack_sysv(const struct sigaltstack *neu,
|
||||
struct sigaltstack *old) {
|
||||
void *b;
|
||||
const void *a;
|
||||
struct sigaltstack_bsd bsd;
|
||||
if (IsLinux()) {
|
||||
a = neu;
|
||||
b = old;
|
||||
} else {
|
||||
if (neu) {
|
||||
sigaltstack2bsd(&bsd, neu);
|
||||
a = &bsd;
|
||||
} else {
|
||||
a = 0;
|
||||
}
|
||||
if (old) {
|
||||
b = &bsd;
|
||||
} else {
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
if (!sys_sigaltstack(a, b)) {
|
||||
if (IsBsd() && old) {
|
||||
sigaltstack2linux(old, &bsd);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets and/or gets alternate signal stack, e.g.
|
||||
*
|
||||
* struct sigaction sa;
|
||||
* struct sigaltstack ss;
|
||||
* ss.ss_flags = 0;
|
||||
* ss.ss_sp = NewCosmoStack();
|
||||
* ss.ss_size = GetStackSize();
|
||||
* ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192;
|
||||
* ss.ss_sp = malloc(ss.ss_size);
|
||||
* sigaltstack(&ss, 0);
|
||||
* sigemptyset(&sa.ss_mask);
|
||||
* sa.sa_flags = SA_ONSTACK;
|
||||
|
@ -75,41 +126,20 @@ static void sigaltstack2linux(struct sigaltstack *linux,
|
|||
*/
|
||||
int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
||||
int rc;
|
||||
void *b;
|
||||
const void *a;
|
||||
struct sigaltstack_bsd bsd;
|
||||
if (IsAsan() && ((old && !__asan_is_valid(old, sizeof(*old))) ||
|
||||
(neu && !__asan_is_valid(neu, sizeof(*neu))))) {
|
||||
rc = efault();
|
||||
} else if (neu && neu->ss_size < MINSIGSTKSZ) {
|
||||
} else if (neu && ((neu->ss_size >> 32) || //
|
||||
(neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) {
|
||||
return einval();
|
||||
} else if (neu && neu->ss_size < __get_minsigstksz()) {
|
||||
rc = enomem();
|
||||
} else if (IsLinux() || IsBsd()) {
|
||||
if (IsLinux()) {
|
||||
a = neu;
|
||||
b = old;
|
||||
} else {
|
||||
if (neu) {
|
||||
sigaltstack2bsd(&bsd, neu);
|
||||
a = &bsd;
|
||||
} else {
|
||||
a = 0;
|
||||
}
|
||||
if (old) {
|
||||
b = &bsd;
|
||||
} else {
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
if ((rc = sys_sigaltstack(a, b)) != -1) {
|
||||
if (IsBsd() && old) {
|
||||
sigaltstack2linux(old, &bsd);
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
if (!(rc = sigaltstack_sysv(neu, old))) {
|
||||
sigaltstack_cosmo(neu, old);
|
||||
}
|
||||
} else {
|
||||
rc = enosys();
|
||||
rc = sigaltstack_cosmo(neu, old);
|
||||
}
|
||||
STRACE("sigaltstack(%s, [%s]) → %d% m", DescribeSigaltstk(0, neu),
|
||||
DescribeSigaltstk(0, old), rc);
|
||||
|
|
|
@ -17,11 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
dontasan sigset_t _sigblockall(void) {
|
||||
sigset_t ss;
|
||||
__repstosb(&ss, -1, sizeof(ss));
|
||||
sigset_t _sigblockall(void) {
|
||||
sigset_t ss = {{-1, -1}};
|
||||
return _sigsetmask(ss);
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
intptr_t __sigchld_thread;
|
||||
static atomic_uint __sigchld_once;
|
||||
static struct CosmoTib __sigchld_tls;
|
||||
|
||||
static textwindows bool __sigchld_check(void) {
|
||||
bool should_signal = false;
|
||||
for (;;) {
|
||||
int pids[64];
|
||||
int64_t handles[64];
|
||||
uint32_t n = __sample_pids(pids, handles, true);
|
||||
if (!n) return should_signal;
|
||||
uint32_t i = WaitForMultipleObjects(n, handles, false, 0);
|
||||
if (i == kNtWaitFailed) return should_signal;
|
||||
if (i == kNtWaitTimeout) return should_signal;
|
||||
i &= ~kNtWaitAbandoned;
|
||||
if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) &&
|
||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) {
|
||||
CloseHandle(handles[i]);
|
||||
__releasefd(pids[i]);
|
||||
} else {
|
||||
g_fds.p[pids[i]].zombie = true;
|
||||
}
|
||||
should_signal = true;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows uint32_t __sigchld_worker(void *arg) {
|
||||
__set_tls_win32(&__sigchld_tls);
|
||||
for (;;) {
|
||||
if (__sigchld_check()) {
|
||||
__sig_notify(SIGCHLD, CLD_EXITED);
|
||||
}
|
||||
SleepEx(100, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows void __sigchld_init(void) {
|
||||
__sigchld_thread = CreateThread(0, 65536, __sigchld_worker, 0, 0, 0);
|
||||
}
|
||||
|
||||
void _init_sigchld(void) {
|
||||
cosmo_once(&__sigchld_once, __sigchld_init);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
|
||||
|
@ -36,11 +37,15 @@
|
|||
|
||||
privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
|
||||
struct ucontext_freebsd *ctx) {
|
||||
int rva, flags;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct Goodies {
|
||||
ucontext_t uc;
|
||||
siginfo_t si;
|
||||
} g;
|
||||
CheckLargeStackAllocation(&g, sizeof(g));
|
||||
#pragma GCC pop_options
|
||||
int rva, flags;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig];
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#ifdef __x86_64__
|
||||
|
||||
privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
|
||||
int i, rva, flags;
|
||||
int rva, flags;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig];
|
||||
|
@ -40,10 +40,6 @@ privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
|
|||
// https://github.com/microsoft/WSL/issues/2555
|
||||
if ((flags & SA_SIGINFO) && UNLIKELY(!ctx->uc_mcontext.fpregs)) {
|
||||
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
long double nan = NAN;
|
||||
__memcpy(ctx->__fpustate.st + i, &nan, 16);
|
||||
}
|
||||
}
|
||||
((sigaction_f)(__executable_start + rva))(sig, info, ctx);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
|
||||
|
@ -36,8 +37,12 @@
|
|||
|
||||
privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si,
|
||||
struct ucontext_netbsd *ctx) {
|
||||
int rva, flags;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
ucontext_t uc;
|
||||
CheckLargeStackAllocation(&uc, sizeof(uc));
|
||||
#pragma GCC pop_options
|
||||
int rva, flags;
|
||||
struct siginfo si2;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
|
||||
|
@ -36,11 +37,15 @@
|
|||
|
||||
privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
|
||||
struct ucontext_openbsd *ctx) {
|
||||
int rva, flags;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct Goodies {
|
||||
ucontext_t uc;
|
||||
struct siginfo si;
|
||||
} g;
|
||||
CheckLargeStackAllocation(&g, sizeof(g));
|
||||
#pragma GCC pop_options
|
||||
int rva, flags;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig];
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/intrin/repstosb.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
|
||||
|
@ -488,11 +489,15 @@ static privileged void linuxssefpustate2xnu(
|
|||
privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
|
||||
struct siginfo_xnu *xnuinfo,
|
||||
struct __darwin_ucontext *xnuctx) {
|
||||
int rva, flags;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct Goodies {
|
||||
ucontext_t uc;
|
||||
siginfo_t si;
|
||||
} g;
|
||||
CheckLargeStackAllocation(&g, sizeof(g));
|
||||
#pragma GCC pop_options
|
||||
int rva, flags;
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig];
|
||||
|
|
|
@ -98,6 +98,8 @@ privileged void __siginfo2cosmo(struct siginfo *si,
|
|||
si->si_addr = si_addr;
|
||||
} else if (si_signo == SIGCHLD) {
|
||||
si->si_status = si_status;
|
||||
si->si_pid = si_pid;
|
||||
si->si_uid = si_uid;
|
||||
} else if (si_signo == SIGALRM) {
|
||||
si->si_timerid = si_timerid;
|
||||
si->si_overrun = si_overrun;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
@ -28,37 +30,43 @@
|
|||
#define GetSigBit(x) (1ull << (((x)-1) & 63))
|
||||
|
||||
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
|
||||
uint64_t x, y, *mask;
|
||||
if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
|
||||
if (__tls_enabled) {
|
||||
mask = &__get_tls()->tib_sigmask;
|
||||
} else {
|
||||
mask = &__sig.mask;
|
||||
}
|
||||
if (old) {
|
||||
old->__bits[0] = *mask;
|
||||
old->__bits[1] = 0;
|
||||
}
|
||||
if (neu) {
|
||||
x = *mask;
|
||||
y = neu->__bits[0];
|
||||
if (how == SIG_BLOCK) {
|
||||
x |= y;
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
x &= ~y;
|
||||
} else {
|
||||
x = y;
|
||||
}
|
||||
x &= ~(0
|
||||
#define M(x) | GetSigBit(x)
|
||||
#include "libc/intrin/sigisprecious.inc"
|
||||
);
|
||||
*mask = x;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
// validate api usage
|
||||
if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
// get address of sigset to modify
|
||||
_Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask;
|
||||
|
||||
// these signals are precious to cosmopolitan
|
||||
uint64_t precious = 0
|
||||
#define M(x) | GetSigBit(x)
|
||||
#include "libc/intrin/sigisprecious.inc"
|
||||
;
|
||||
|
||||
// handle read-only case
|
||||
uint64_t oldmask;
|
||||
if (neu) {
|
||||
uint64_t input = neu->__bits[0] & ~precious;
|
||||
if (how == SIG_BLOCK) {
|
||||
oldmask = atomic_fetch_or_explicit(mask, input, memory_order_acq_rel);
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
oldmask = atomic_fetch_and_explicit(mask, input, memory_order_acq_rel);
|
||||
} else { // SIG_SETMASK
|
||||
oldmask = atomic_exchange_explicit(mask, input, memory_order_acq_rel);
|
||||
}
|
||||
} else {
|
||||
oldmask = atomic_load_explicit(mask, memory_order_acquire);
|
||||
}
|
||||
|
||||
// return old signal mask to caller
|
||||
if (old) {
|
||||
old->__bits[0] = oldmask;
|
||||
old->__bits[1] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -60,7 +60,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
|||
} else if (IsMetal() || IsWindows()) {
|
||||
rc = __sig_mask(how, opt_set, &old);
|
||||
if (_weaken(__sig_check)) {
|
||||
_weaken(__sig_check)(kSigOpRestartable);
|
||||
_weaken(__sig_check)();
|
||||
}
|
||||
} else {
|
||||
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);
|
||||
|
|
|
@ -16,19 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
dontasan sigset_t _sigsetmask(sigset_t neu) {
|
||||
sigset_t _sigsetmask(sigset_t neu) {
|
||||
sigset_t res;
|
||||
if (IsMetal() || IsWindows()) {
|
||||
__sig_mask(SIG_SETMASK, &neu, &res);
|
||||
} else {
|
||||
npassert(!sys_sigprocmask(SIG_SETMASK, &neu, &res));
|
||||
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -16,19 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -50,7 +46,6 @@ int sigsuspend(const sigset_t *ignore) {
|
|||
int rc;
|
||||
const sigset_t *arg;
|
||||
sigset_t save, mask = {0};
|
||||
STRACE("sigsuspend(%s) → ...", DescribeSigset(0, ignore));
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
|
||||
|
@ -74,36 +69,26 @@ int sigsuspend(const sigset_t *ignore) {
|
|||
rc = sys_sigsuspend(arg, 8);
|
||||
} else {
|
||||
__sig_mask(SIG_SETMASK, arg, &save);
|
||||
#if defined(SYSDEBUG) && _POLLTRACE
|
||||
long ms = 0;
|
||||
long totoms = 0;
|
||||
#endif
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
do {
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR");
|
||||
continue;
|
||||
}
|
||||
#if defined(SYSDEBUG) && _POLLTRACE
|
||||
ms += __SIG_POLLING_INTERVAL_MS;
|
||||
if (ms >= __SIG_LOGGING_INTERVAL_MS) {
|
||||
totoms += ms, ms = 0;
|
||||
POLLTRACE("... sigsuspending for %'lums...", totoms);
|
||||
}
|
||||
#endif
|
||||
} while (1);
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
__sig_mask(SIG_SETMASK, &save, 0);
|
||||
}
|
||||
} else {
|
||||
// TODO(jart): sigsuspend metal support
|
||||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
STRACE("...sigsuspend → %d% m", rc);
|
||||
STRACE("sigsuspend(%s) → %d% m", DescribeSigset(0, ignore), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue