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:
Justine Tunney 2023-09-18 20:44:45 -07:00
parent c4eb838516
commit ec480f5aa0
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
638 changed files with 7925 additions and 8282 deletions

View file

@ -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 \

View file

@ -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;

View file

@ -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
View 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

View file

@ -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

View file

@ -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:

View file

@ -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};

View file

@ -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;
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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
View 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();
}

View file

@ -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

View file

@ -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;

View file

@ -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"

View file

@ -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;
}

View file

@ -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
View 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);
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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;
}
}

View file

@ -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]));

View file

@ -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__ */

View file

@ -40,5 +40,5 @@ wontreturn void abort(void) {
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
notpossible;
_Exit(128 + SIGABRT);
}

View file

@ -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) */

View file

@ -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 *);

View file

@ -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)))

View file

@ -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) &&

View file

@ -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));
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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 || //

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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));
}
}

View file

@ -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
View 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;
}

View file

@ -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"

View file

@ -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__ */

View file

@ -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;

View file

@ -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();

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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)) {

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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();
}

View file

@ -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

View file

@ -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;

View file

@ -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"

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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();

View file

@ -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
}

View file

@ -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;

View file

@ -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 */
}

View file

@ -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__ */

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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()) {

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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
View 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__ */

View file

@ -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) */

View file

@ -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));

View file

@ -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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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];

View file

@ -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);
}

View file

@ -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) {

View file

@ -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];

View file

@ -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];

View file

@ -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;

View file

@ -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__ */

View file

@ -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);

View file

@ -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;
}

View file

@ -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