cosmopolitan/libc/sysv/sysv.c

158 lines
5.5 KiB
C
Raw Normal View History

/*-*- 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/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#ifndef __x86_64__
// todo(jart): dismal llvm
register long x0 asm("x0");
register long x1 asm("x1");
register long x2 asm("x2");
register long x3 asm("x3");
register long x4 asm("x4");
register long x5 asm("x5");
register long sysv_ordinal asm("x8");
register long xnu_ordinal asm("x16");
register long cosmo_tls_register asm("x28");
void report_cancellation_point(void);
dontinline long systemfive_cancel(void) {
return _pthread_cancel_sys();
}
// special region of executable memory where cancellation is safe
dontinline long systemfive_cancellable(void) {
// check (1) this is a cancellation point
// plus (2) cancellations aren't disabled
struct PosixThread *pth = 0;
struct CosmoTib *tib = __get_tls();
if (cosmo_tls_register && //
_weaken(_pthread_cancel_sys) && //
!(tib->tib_flags & PT_NOCANCEL) && //
(pth = (struct PosixThread *)tib->tib_pthread)) {
// check if cancellation is already pending
if (atomic_load_explicit(&pth->cancelled, memory_order_acquire)) {
return systemfive_cancel();
}
#if IsModeDbg()
if (!(tib->tib_flags & PT_INCANCEL)) {
if (_weaken(report_cancellation_point)) {
_weaken(report_cancellation_point)();
}
__builtin_trap();
}
#endif
}
// invoke cancellable system call
// this works for both linux and bsd
asm volatile("mov\tx9,0\n\t" // clear carry flag
"adds\tx9,x9,0\n\t" // clear carry flag
"svc\t0\n"
"systemfive_cancellable_end:\n\t"
".globl\tsystemfive_cancellable_end\n\t"
"bcs\t1f\n\t"
"b\t2f\n1:\t"
"neg\tx0,x0\n2:"
: /* global output */
: /* global inputs */
: "x9", "memory");
// if it succeeded then we're done
if (x0 < -4095ul) {
return x0;
}
// check if i/o call was interrupted by sigthr
if (pth && x0 == -EINTR &&
atomic_load_explicit(&pth->cancelled, memory_order_acquire)) {
return systemfive_cancel();
}
// otherwise go down error path
return _sysret(x0);
}
/**
* System Five System Call Support.
*
* This supports POSIX thread cancellation only when the caller flips a
* bit in TLS storage that indicates we're inside a cancellation point.
*
* @param x0 is first argument
* @param x1 is second argument
* @param x2 is third argument
* @param x3 is fourth argument
* @param x4 is fifth argument
* @param x5 is sixth argument
* @param sysv_ordinal is linux ordinal
* @param xnu_ordinal is xnu ordinal
* @return x0
*/
long systemfive(void) {
// handle special cases
if (IsLinux()) {
if (sysv_ordinal == 0xfff) {
return _sysret(-ENOSYS);
}
if (sysv_ordinal & 0x800) {
sysv_ordinal &= ~0x800;
return systemfive_cancellable();
}
}
if (IsXnu()) {
if (xnu_ordinal == 0xfff) {
return _sysret(-ENOSYS);
}
if (xnu_ordinal & 0x800) {
xnu_ordinal &= ~0x800;
return systemfive_cancellable();
}
}
// invoke non-blocking system call
// this works for both linux and bsd
asm volatile("mov\tx9,0\n\t" // clear carry flag
"adds\tx9,x9,0\n\t" // clear carry flag
"svc\t0\n\t"
"bcs\t1f\n\t"
"b\t2f\n1:\t"
"neg\tx0,x0\n2:"
: /* global output */
: /* global inputs */
: "x9", "memory");
// check result
if (x0 < -4095ul) {
return x0;
} else {
return _sysret(x0);
}
}
#endif /* __x86_64__ */