text:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>

static const char defshells[] = "/bin/sh\n/bin/csh\n";

static char *line;
static size_t linesize;
static FILE *f;

void endusershell(void)
{
	if (f) fclose(f);
	f = 0;
}

void setusershell(void)
{
	if (!f) f = fopen("/etc/shells", "rbe");
	if (!f) f = fmemopen((void *)defshells, sizeof defshells - 1, "rb");
}

char *getusershell(void)
{
	ssize_t l;
	if (!f) setusershell();
	if (!f) return 0;
	l = getline(&line, &linesize, f);
	if (l <= 0) return 0;
	if (line[l-1]=='\n') line[l-1]=0;
	return line;
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "libc.h"

int getpagesize(void)
{
	return PAGE_SIZE;
}

repo_language: C

mime_type: text/x-c


text:
#include <sys/resource.h>
#include <ulimit.h>
#include <stdarg.h>

long ulimit(int cmd, ...)
{
	struct rlimit rl;
	getrlimit(RLIMIT_FSIZE, &rl);
	if (cmd == UL_SETFSIZE) {
		long val;
		va_list ap;
		va_start(ap, cmd);
		val = va_arg(ap, long);
		va_end(ap);
		rl.rlim_cur = 512ULL * val;
		if (setrlimit(RLIMIT_FSIZE, &rl)) return -1;
	}
	return rl.rlim_cur / 512;
}


text:
#include <ftw.h>
#include "libc.h"

int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int fd_limit)
{
	/* The following cast assumes that calling a function with one
	 * argument more than it needs behaves as expected. This is
	 * actually undefined, but works on all real-world machines. */
	return nftw(path, (int (*)())fn, fd_limit, FTW_PHYS);
}

LFS64(ftw);


text:
#define _GNU_SOURCE
#include <sys/stat.h>
#include <sys/time.h>

int futimes(int fd, const struct timeval tv[2])
{
	struct timespec times[2];
	times[0].tv_sec  = tv[0].tv_sec;
	times[0].tv_nsec = tv[0].tv_usec * 1000;
	times[1].tv_sec  = tv[1].tv_sec;
	times[1].tv_nsec = tv[1].tv_usec * 1000;
	return futimens(fd, times);
}


text:
#include <stropts.h>
#include <fcntl.h>

int isastream(int fd)
{
	return fcntl(fd, F_GETFD) < 0 ? -1 : 0;
}


text:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int getloadavg(double *a, int n)
{
	int i;
	double b[3];
	FILE *f = fopen("/proc/loadavg", "rbe");
	if (!f) return -1;
	i = fscanf(f, "%lf %lf %lf", b, b+1, b+2);
	fclose(f);
	if (n > i) n = i;
	if (n < 0) return -1;
	memcpy(a, b, n * sizeof *a);
	return n;
}


text:
#include <utmpx.h>
#include <stddef.h>
#include "libc.h"

void endutxent(void)
{
}

void setutxent(void)
{
}

struct utmpx *getutxent(void)
{
	return NULL;
}

struct utmpx *getutxid(const struct utmpx *ut)
{
	return NULL;
}

struct utmpx *getutxline(const struct utmpx *ut)
{
	return NULL;
}

struct utmpx *pututxline(const struct utmpx *ut)
{
	return NULL;
}

void updwtmpx(const char *f, const struct utmpx *u)
{
}

weak_alias(endutxent, endutent);
weak_alias(setutxent, setutent);
weak_alias(getutxent, getutent);
weak_alias(getutxid, getutid);
weak_alias(getutxline, getutline);
weak_alias(pututxline, pututline);
weak_alias(updwtmpx, updwtmp);


text:
#define _GNU_SOURCE
#include <unistd.h>
#include <limits.h>
#include <sys/resource.h>

int getdtablesize(void)
{
	struct rlimit rl;
	getrlimit(RLIMIT_NOFILE, &rl);
	return rl.rlim_max < INT_MAX ? rl.rlim_max : INT_MAX;
}


text:
#define _GNU_SOURCE
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>

int lutimes(const char *filename, const struct timeval tv[2])
{
	struct timespec times[2];
	times[0].tv_sec  = tv[0].tv_sec;
	times[0].tv_nsec = tv[0].tv_usec * 1000;
	times[1].tv_sec  = tv[1].tv_sec;
	times[1].tv_nsec = tv[1].tv_usec * 1000;
	return utimensat(AT_FDCWD, filename, times, AT_SYMLINK_NOFOLLOW);
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include "libc.h"

int euidaccess(const char *filename, int amode)
{
	return faccessat(AT_FDCWD, filename, amode, AT_EACCESS);
}

weak_alias(euidaccess, eaccess);


text:
#define _GNU_SOURCE
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

char *getpass(const char *prompt)
{
	int fd;
	struct termios s, t;
	ssize_t l;
	static char password[128];

	if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0) return 0;

	tcgetattr(fd, &t);
	s = t;
	t.c_lflag &= ~(ECHO|ISIG);
	t.c_lflag |= ICANON;
	t.c_iflag &= ~(INLCR|IGNCR);
	t.c_iflag |= ICRNL;
	tcsetattr(fd, TCSAFLUSH, &t);
	tcdrain(fd);

	dprintf(fd, "%s", prompt);

	l = read(fd, password, sizeof password);
	if (l >= 0) {
		if (l > 0 && password[l-1] == '\n') l--;
		password[l] = 0;
	}

	tcsetattr(fd, TCSAFLUSH, &s);

	dprintf(fd, "\n");
	close(fd);

	return l<0 ? 0 : password;
}


text:
#include <err.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

extern char *__progname;

void vwarn(const char *fmt, va_list ap)
{
	fprintf (stderr, "%s: ", __progname);
	if (fmt) {
		vfprintf(stderr, fmt, ap);
		fputs (": ", stderr);
	}
	perror(0);
}

void vwarnx(const char *fmt, va_list ap)
{
	fprintf (stderr, "%s: ", __progname);
	if (fmt) vfprintf(stderr, fmt, ap);
	putc('\n', stderr);
}

_Noreturn void verr(int status, const char *fmt, va_list ap)
{
	vwarn(fmt, ap);
	exit(status);
}

_Noreturn void verrx(int status, const char *fmt, va_list ap)
{
	vwarnx(fmt, ap);
	exit(status);
}

void warn(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vwarn(fmt, ap);
	va_end(ap);
}

void warnx(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vwarnx(fmt, ap);
	va_end(ap);
}

_Noreturn void err(int status, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	verr(status, fmt, ap);
	va_end(ap);
}

_Noreturn void errx(int status, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	verrx(status, fmt, ap);
	va_end(ap);
}


text:
#define _GNU_SOURCE
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>

char *cuserid(char *buf)
{
	struct passwd pw, *ppw;
	long pwb[256];
	if (getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw))
		return 0;
	snprintf(buf, L_cuserid, "%s", pw.pw_name);
	return buf;
}


text:
#define _BSD_SOURCE
#include <stdlib.h>
#include "libc.h"

void *valloc(size_t size)
{
	return memalign(PAGE_SIZE, size);
}


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>

int daemon(int nochdir, int noclose)
{
	if (!nochdir && chdir("/"))
		return -1;
	if (!noclose) {
		int fd, failed = 0;
		if ((fd = open("/dev/null", O_RDWR)) < 0) return -1;
		if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0)
			failed++;
		if (fd > 2) close(fd);
		if (failed) return -1;
	}

	switch(fork()) {
	case 0: break;
	case -1: return -1;
	default: _exit(0);
	}

	if (setsid() < 0) return -1;

	switch(fork()) {
	case 0: break;
	case -1: return -1;
	default: _exit(0);
	}

	return 0;
}


text:
#include <signal.h>

void (*sigset(int sig, void (*handler)(int)))(int)
{
	struct sigaction sa, sa_old;
	sigset_t mask;

	sigemptyset(&mask);
	if (sigaddset(&mask, sig) < 0)
		return SIG_ERR;

	if (handler == SIG_HOLD) {
		if (sigaction(sig, 0, &sa_old) < 0)
			return SIG_ERR;
		if (sigprocmask(SIG_BLOCK, &mask, &mask) < 0)
			return SIG_ERR;
	} else {
		sa.sa_handler = handler;
		sa.sa_flags = 0;
		sigemptyset(&sa.sa_mask);
		if (sigaction(sig, &sa, &sa_old) < 0)
			return SIG_ERR;
		if (sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0)
			return SIG_ERR;
	}
	return sigismember(&mask, sig) ? SIG_HOLD : sa_old.sa_handler;
}


text:
#include <setjmp.h>
#include <signal.h>
#include "libc.h"

/* !!! This function will not work unless the compiler performs
 * tail call optimization. Machine-specific asm versions should
 * be created instead even though the workaround (tail call)
 * is entirely non-machine-specific... */

int sigsetjmp(sigjmp_buf buf, int save)
{
	if ((buf->__fl = save))
		pthread_sigmask(SIG_SETMASK, 0, (sigset_t *)buf->__ss);
	return setjmp(buf);
}

weak_alias(sigsetjmp, __sigsetjmp);


text:
#include <sys/time.h>
#include "syscall.h"

int getitimer(int which, struct itimerval *old)
{
	return syscall(SYS_getitimer, which, old);
}


text:
#include <signal.h>

int sigpause(int sig)
{
	sigset_t mask;
	sigprocmask(0, 0, &mask);
	sigdelset(&mask, sig);
	return sigsuspend(&mask);
}


text:
#include <signal.h>
#include "syscall.h"

int kill(pid_t pid, int sig)
{
	return syscall(SYS_kill, pid, sig);
}


text:
#include <signal.h>
#include <errno.h>

int sigdelset(sigset_t *set, int sig)
{
	unsigned s = sig-1;
	if (s >= _NSIG-1 || sig-32U < 3) {
		errno = EINVAL;
		return -1;
	}
	set->__bits[s/8/sizeof *set->__bits] &=~(1UL<<(s&8*sizeof *set->__bits-1));
	return 0;
}


text:
#include <setjmp.h>
#include <signal.h>
#include "syscall.h"
#include "pthread_impl.h"

_Noreturn void siglongjmp(sigjmp_buf buf, int ret)
{
	if (buf->__fl) __restore_sigs(buf->__ss);
	longjmp(buf, ret);
}


text:
#include <signal.h>
#include <limits.h>

int sigfillset(sigset_t *set)
{
#if ULONG_MAX == 0xffffffff
	set->__bits[0] = 0x7ffffffful;
	set->__bits[1] = 0xfffffffcul;
	if (_NSIG > 65) {
		set->__bits[2] = 0xfffffffful;
		set->__bits[3] = 0xfffffffful;
	}
#else
	set->__bits[0] = 0xfffffffc7ffffffful;
	if (_NSIG > 65) set->__bits[1] = 0xfffffffffffffffful;
#endif
	return 0;
}


text:
#include <signal.h>

int siginterrupt(int sig, int flag)
{
	struct sigaction sa;

	sigaction(sig, 0, &sa);
	if (flag) sa.sa_flags &= ~SA_RESTART;
	else sa.sa_flags |= SA_RESTART;

	return sigaction(sig, &sa, 0);
}


text:
#include <signal.h>
#include <errno.h>
#include "syscall.h"

int sigaltstack(const stack_t *restrict ss, stack_t *restrict old)
{
	if (ss) {
		if (ss->ss_size < MINSIGSTKSZ) {
			errno = ENOMEM;
			return -1;
		}
		if (ss->ss_flags & ~SS_DISABLE) {
			errno = EINVAL;
			return -1;
		}
	}
	return syscall(SYS_sigaltstack, ss, old);
}


text:
#include <signal.h>
#include "syscall.h"
#include "libc.h"

int __sigaction(int, const struct sigaction *, struct sigaction *);

void (*signal(int sig, void (*func)(int)))(int)
{
	struct sigaction sa_old, sa = { .sa_handler = func, .sa_flags = SA_RESTART };
	if (__sigaction(sig, &sa, &sa_old) < 0)
		return SIG_ERR;
	return sa_old.sa_handler;
}

weak_alias(signal, bsd_signal);
weak_alias(signal, __sysv_signal);


text:
#include <signal.h>
#include "syscall.h"
#include "libc.h"

int sigsuspend(const sigset_t *mask)
{
	return syscall_cp(SYS_rt_sigsuspend, mask, _NSIG/8);
}


text:
#include <signal.h>

int sigignore(int sig)
{
	struct sigaction sa;

	sigemptyset(&sa.sa_mask);
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	return sigaction(sig, &sa, 0);
}


text:
#include <signal.h>
#include <errno.h>

int sigaddset(sigset_t *set, int sig)
{
	unsigned s = sig-1;
	if (s >= _NSIG-1 || sig-32U < 3) {
		errno = EINVAL;
		return -1;
	}
	set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
	return 0;
}


text:
#include <signal.h>
#include <errno.h>

int killpg(pid_t pgid, int sig)
{
	if (pgid < 0) {
		errno = EINVAL;
		return -1;
	}
	return kill(-pgid, sig);
}


text:
#include <signal.h>
#include "syscall.h"

int sigpending(sigset_t *set)
{
	return syscall(SYS_rt_sigpending, set, _NSIG/8);
}


text:
#define _GNU_SOURCE
#include <signal.h>
#include <string.h>

int sigisemptyset(const sigset_t *set)
{
	static const unsigned long zeroset[_NSIG/8/sizeof(long)];
	return !memcmp(set, &zeroset, _NSIG/8);
}


text:
#include <stdio.h>
#include <string.h>
#include <signal.h>

void psiginfo(const siginfo_t *si, const char *msg)
{
	char *s = strsignal(si->si_signo);
	if (msg) fprintf(stderr, "%s: %s\n", msg, s);
	else fprintf(stderr, "%s\n", s);
}


text:
int __libc_current_sigrtmin()
{
	return 35;
}


text:
#include <signal.h>

int sigismember(const sigset_t *set, int sig)
{
	unsigned s = sig-1;
	if (s >= _NSIG-1) return 0;
	return !!(set->__bits[s/8/sizeof *set->__bits] & 1UL<<(s&8*sizeof *set->__bits-1));
}


text:
#include <signal.h>
#include <errno.h>
#include <string.h>
#include "syscall.h"
#include "pthread_impl.h"
#include "libc.h"
#include "ksigaction.h"

void __restore(), __restore_rt();

static int unmask_done;
static unsigned long handler_set[_NSIG/(8*sizeof(long))];

void __get_handler_set(sigset_t *set)
{
	memcpy(set, handler_set, sizeof handler_set);
}

int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
	struct k_sigaction ksa, ksa_old;
	if (sig >= (unsigned)_NSIG) {
		errno = EINVAL;
		return -1;
	}
	if (sa) {
		if ((uintptr_t)sa->sa_handler > 1UL) {
			a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
				1UL<<(sig-1)%(8*sizeof(long)));

			/* If pthread_create has not yet been called,
			 * implementation-internal signals might not
			 * yet have been unblocked. They must be
			 * unblocked before any signal handler is
			 * installed, so that an application cannot
			 * receive an illegal sigset_t (with them
			 * blocked) as part of the ucontext_t passed
			 * to the signal handler. */
			if (!libc.threaded && !unmask_done) {
				__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
					SIGPT_SET, 0, _NSIG/8);
				unmask_done = 1;
			}
		}
		ksa.handler = sa->sa_handler;
		ksa.flags = sa->sa_flags | SA_RESTORER;
		ksa.restorer = (sa->sa_flags & SA_SIGINFO) ? __restore_rt : __restore;
		memcpy(&ksa.mask, &sa->sa_mask, sizeof ksa.mask);
	}
	if (syscall(SYS_rt_sigaction, sig, sa?&ksa:0, old?&ksa_old:0, sizeof ksa.mask))
		return -1;
	if (old) {
		old->sa_handler = ksa_old.handler;
		old->sa_flags = ksa_old.flags;
		memcpy(&old->sa_mask, &ksa_old.mask, sizeof ksa_old.mask);
	}
	return 0;
}

int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
	if (sig-32U < 3) {
		errno = EINVAL;
		return -1;
	}
	return __libc_sigaction(sig, sa, old);
}

weak_alias(__sigaction, sigaction);


text:
#define _GNU_SOURCE
#include <signal.h>

#define SST_SIZE (_NSIG/8/sizeof(long))

int sigorset(sigset_t *dest, const sigset_t *left, const sigset_t *right)
{
	unsigned long i = 0, *d = (void*) dest, *l = (void*) left, *r = (void*) right;
	for(; i < SST_SIZE; i++) d[i] = l[i] | r[i];
	return 0;
}


text:
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "syscall.h"
#include "pthread_impl.h"

int sigqueue(pid_t pid, int sig, const union sigval value)
{
	siginfo_t si;
	sigset_t set;
	int r;
	memset(&si, 0, sizeof si);
	si.si_signo = sig;
	si.si_code = SI_QUEUE;
	si.si_value = value;
	si.si_uid = getuid();
	__block_app_sigs(&set);
	si.si_pid = getpid();
	r = syscall(SYS_rt_sigqueueinfo, pid, sig, &si);
	__restore_sigs(&set);
	return r;
}


text:
#include <signal.h>
#include <errno.h>
#include "syscall.h"
#include "libc.h"

int sigtimedwait(const sigset_t *restrict mask, siginfo_t *restrict si, const struct timespec *restrict timeout)
{
	int ret;
	do ret = syscall_cp(SYS_rt_sigtimedwait, mask,
		si, timeout, _NSIG/8);
	while (ret<0 && errno==EINTR);
	return ret;
}


text:
#include <signal.h>
#include <string.h>

int sigemptyset(sigset_t *set)
{
	set->__bits[0] = 0;
	if (sizeof(long)==4 || _NSIG > 65) set->__bits[1] = 0;
	if (sizeof(long)==4 && _NSIG > 65) {
		set->__bits[2] = 0;
		set->__bits[3] = 0;
	}
	return 0;
}


text:
#include <signal.h>

int sigwaitinfo(const sigset_t *restrict mask, siginfo_t *restrict si)
{
	return sigtimedwait(mask, si, 0);
}


text:
#include <signal.h>

int __libc_current_sigrtmax()
{
	return _NSIG-1;
}


text:
#include <sys/time.h>
#include "syscall.h"

int setitimer(int which, const struct itimerval *restrict new, struct itimerval *restrict old)
{
	return syscall(SYS_setitimer, which, new, old);
}


text:
#define _GNU_SOURCE
#include <signal.h>

#define SST_SIZE (_NSIG/8/sizeof(long))

int sigandset(sigset_t *dest, const sigset_t *left, const sigset_t *right)
{
	unsigned long i = 0, *d = (void*) dest, *l = (void*) left, *r = (void*) right;
	for(; i < SST_SIZE; i++) d[i] = l[i] & r[i];
	return 0;
}


text:
#include <signal.h>
#include <errno.h>

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict old)
{
	int r = pthread_sigmask(how, set, old);
	if (!r) return r;
	errno = r;
	return -1;
}


text:
#include "pthread_impl.h"
#include "syscall.h"
#include <signal.h>

static const unsigned long all_mask[] = {
#if ULONG_MAX == 0xffffffff && _NSIG == 129
	-1UL, -1UL, -1UL, -1UL
#elif ULONG_MAX == 0xffffffff
	-1UL, -1UL
#else
	-1UL
#endif
};

static const unsigned long app_mask[] = {
#if ULONG_MAX == 0xffffffff
#if _NSIG == 65
	0x7fffffff, 0xfffffffc
#else
	0x7fffffff, 0xfffffffc, -1UL, -1UL
#endif
#else
#if _NSIG == 65
	0xfffffffc7fffffff
#else
	0xfffffffc7fffffff, -1UL
#endif
#endif
};

void __block_all_sigs(void *set)
{
	__syscall(SYS_rt_sigprocmask, SIG_BLOCK, &all_mask, set, _NSIG/8);
}

void __block_app_sigs(void *set)
{
	__syscall(SYS_rt_sigprocmask, SIG_BLOCK, &app_mask, set, _NSIG/8);
}

void __restore_sigs(void *set)
{
	__syscall(SYS_rt_sigprocmask, SIG_SETMASK, set, 0, _NSIG/8);
}


text:
#include <signal.h>

int sigrelse(int sig)
{
	sigset_t mask;

	sigemptyset(&mask);
	if (sigaddset(&mask, sig) < 0) return -1;
	return sigprocmask(SIG_UNBLOCK, &mask, 0);
}


text:
#include <stdio.h>
#include <string.h>
#include <signal.h>

void psignal(int sig, const char *msg)
{
	char *s = strsignal(sig);
	if (msg) fprintf(stderr, "%s: %s\n", msg, s);
	else fprintf(stderr, "%s\n", s);
}


text:
#include <signal.h>
#include <stdint.h>
#include "syscall.h"
#include "pthread_impl.h"

int raise(int sig)
{
	int pid, tid, ret;
	sigset_t set;
	__block_app_sigs(&set);
	tid = __syscall(SYS_gettid);
	pid = __syscall(SYS_getpid);
	ret = syscall(SYS_tgkill, pid, tid, sig);
	__restore_sigs(&set);
	return ret;
}


text:
/* These functions will not work, but suffice for targets where the
 * kernel sigaction structure does not actually use sa_restorer. */

void __restore()
{
}

void __restore_rt()
{
}


text:
#include <signal.h>

int sigwait(const sigset_t *restrict mask, int *restrict sig)
{
	siginfo_t si;
	if (sigtimedwait(mask, &si, 0) < 0)
		return -1;
	*sig = si.si_signo;
	return 0;
}


text:
#include <signal.h>

int sighold(int sig)
{
	sigset_t mask;

	sigemptyset(&mask);
	if (sigaddset(&mask, sig) < 0) return -1;
	return sigprocmask(SIG_BLOCK, &mask, 0);
}


text:
	.global sigsetjmp
	.global __sigsetjmp
	.type sigsetjmp,%function
	.type __sigsetjmp,%function
sigsetjmp:
__sigsetjmp:
	#int sigsetjmp(sigjmp_buf buf, int save)
	#		r3		r4
	#0) store save into buf->__fl
	stw 4, 448(3)
	#1) compare save with 0
	cmpwi cr7, 4, 0
	#2) if its 0, goto setjmp code
	beq- cr7, 1f
	#3) else: we must call pthread_sigmask(SIG_SETMASK, 0, (sigset_t *)buf->__ss);
	# store non-volatile regs 30, 31 into the setjmp buf
	stw 30, 0(3)
	stw 31, 4(3)
	# use them to store the pointer to the jmpbuf and the link reg
	mr 30, 3
	mflr 31

	# put pointer to ss buf into r5 (3rd arg)
	addi 5, 3, 452
	# put "2" i.e. SIG_SETMASK in r3
	li 3, 2
	li 4, 0
	bl pthread_sigmask

	#restore jmpbuf pointer and link reg
	mr 3, 30
	mtlr 31
	#resore non-volatile regs
	lwz 30, 0(3)
	lwz 31, 4(3)

1:
	b setjmp


text:
	.global __restore
	.type __restore,%function
__restore:
	li      0, 119 #__NR_sigreturn
	sc

	.global __restore_rt
	.type __restore_rt,%function
__restore_rt:
	li      0, 172 # __NR_rt_sigreturn
	sc


text:
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	mov.l r5, @(36,r4)
	tst r5, r5
	bf  2f

	sts.l pr, @-r15
	mov.l r4, @-r15
	mov r4, r6
	add #40, r6
	mov #0, r5
	mov #2, r4
	mov.l L1, r0
	bsrf  r0
	 nop
1:	mov.l @r15+, r4
	lds.l @r15+, pr

2:	mov.l L2, r0
	braf  r0
	 nop
3:

.align 2
L1:	.long pthread_sigmask@PLT-(1b-.)
L2:	.long setjmp@PLT-(3b-.)


text:
.global __restore
.type   __restore, @function
__restore:
	mov   #119, r3  !__NR_sigreturn
	trapa #16

	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0

.global __restore_rt
.type   __restore_rt, @function
__restore_rt:
	mov   #100, r3  !__NR_rt_sigreturn
	add   #73, r3
	trapa #16

	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0


text:
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	swi     r6, r5, 72
	beqi    r6, setjmp@PLT

	addi    r1, r1, -32
	swi     r15, r1, 28
	swi     r5, r1, 24
	addi    r7, r5, 76
	add     r6, r0, r0
	brlid   r15, sigprocmask@PLT
	ori     r5, r0, 2

	lwi     r15, r1, 28
	lwi     r5, r1, 24
	brid    setjmp@PLT
	addi    r1, r1, 32


text:
.global __restore
.type __restore,@function
__restore:
	ori     r12, r0, 119
	brki    r14, 0x8

.global __restore_rt
.type __restore_rt,@function
__restore_rt:
	ori     r12, r0, 173
	brki    r14, 0x8


text:
.set noreorder

.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	lui $gp, %hi(_gp_disp)
	addiu $gp, %lo(_gp_disp)
	beq $5, $0, 1f
	addu $gp, $gp, $25
	subu $sp, $sp, 32
	sw $4, 20($sp)
	sw $ra, 24($sp)
	sw $gp, 28($sp)
	addu $6, $4, 108
	li $5, 0
	li $4, 1
	sw $4, -4($6)
	lw $25, %call16(sigprocmask)($gp)
	jalr $25
	nop
	lw $gp, 28($sp)
	lw $ra, 24($sp)
	lw $4, 20($sp)
	addu $sp, $sp, 32
1:	lw $25, %call16(setjmp)($gp)
	jr $25
	nop


text:
.set noreorder

.global __restore_rt
.type   __restore_rt,@function
__restore_rt:
	li $2, 4193
	syscall

.global __restore
.type   __restore,@function
__restore:
	li $2, 4119
	syscall


text:
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	mov 4(%esp),%eax
	mov 8(%esp),%ecx
	mov %ecx,24(%eax)
	jecxz 1f
	add $28,%eax
	push %eax
	push $0
	push $2
	call sigprocmask
	add $12,%esp
1:	jmp setjmp


text:
.global __restore
.type __restore,@function
__restore:
	popl %eax
	movl $119, %eax
	int $0x80

.global __restore_rt
.type __restore_rt,@function
__restore_rt:
	movl $173, %eax
	int $0x80


text:
/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	andl %esi,%esi
	movq %rsi,64(%rdi)
	jz 1f
	pushq %rdi
	leaq 72(%rdi),%rdx
	xorl %esi,%esi
	movl $2,%edi
	call sigprocmask
	popq %rdi
1:	jmp setjmp


text:
.global __restore_rt
.global __restore
.type __restore_rt,@function
.type __restore,@function
__restore_rt:
__restore:
	movl $0x40000201, %eax /* SYS_rt_sigreturn */
	syscall


text:
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,%function
.type __sigsetjmp,%function
sigsetjmp:
__sigsetjmp:
	str a2,[a1,#256]
	tst a2,a2
	beq setjmp
	push {a1,lr}
	add a3,a1,#260
	mov a2,#0
	mov a1,#2
	bl sigprocmask
	pop {a1,lr}
	b setjmp


text:
.global __restore
.type __restore,%function
__restore:
	mov r7,#119
	swi 0x0

.global __restore_rt
.type __restore_rt,%function
__restore_rt:
	mov r7,#173
	swi 0x0


text:
.global sigsetjmp
.global __sigsetjmp
.type sigsetjmp,@function
.type __sigsetjmp,@function
sigsetjmp:
__sigsetjmp:
	l.sfeq	r4, r0
	l.bf	plt(setjmp)
	 l.sw	52(r3), r4	/* buf->__fl = save */

	l.addi	r1, r1, -8
	l.sw	0(r1), r9
	l.sw	4(r1), r3
	l.addi	r5, r3, 56	/* buf->__ss */
	l.add	r4, r0, r0
	l.jal	plt(sigprocmask)
	 l.ori	r3, r0, 2	/* SIG_SETMASK */

	l.lwz	r9, 0(r1)
	l.lwz	r3, 4(r1)
	l.j	plt(setjmp)
	 l.addi	r1, r1, 8


text:
.global __restore_rt
.global __restore
.type __restore_rt,@function
.type __restore,@function
__restore_rt:
__restore:
	movl $15, %eax
	syscall


text:
#include <stdlib.h>
#include <string.h>

extern unsigned short __seed48[7];

void lcong48(unsigned short p[7])
{
	memcpy(__seed48, p, sizeof __seed48);
}


text:
#include <stdlib.h>

void srand48(long seed)
{
	seed48((unsigned short [3]){ 0x330e, seed, seed>>16 });
}


text:
#include <stdlib.h>
#include <stdint.h>
#include "libc.h"

/*
this code uses the same lagged fibonacci generator as the
original bsd random implementation except for the seeding
which was broken in the original
*/

static uint32_t init[] = {
0x00000000,0x5851f42d,0xc0b18ccf,0xcbb5f646,
0xc7033129,0x30705b04,0x20fd5db4,0x9a8b7f78,
0x502959d8,0xab894868,0x6c0356a7,0x88cdb7ff,
0xb477d43f,0x70a3a52b,0xa8e4baf1,0xfd8341fc,
0x8ae16fd9,0x742d2f7a,0x0d1f0796,0x76035e09,
0x40f7702c,0x6fa72ca5,0xaaa84157,0x58a0df74,
0xc74a0364,0xae533cc4,0x04185faf,0x6de3b115,
0x0cab8628,0xf043bfa4,0x398150e9,0x37521657};

static int n = 31;
static int i = 3;
static int j = 0;
static uint32_t *x = init+1;
static int lock[2];

static uint32_t lcg31(uint32_t x) {
	return (1103515245*x + 12345) & 0x7fffffff;
}

static uint64_t lcg64(uint64_t x) {
	return 6364136223846793005ull*x + 1;
}

static void *savestate() {
	x[-1] = (n<<16)|(i<<8)|j;
	return x-1;
}

static void loadstate(uint32_t *state) {
	x = state+1;
	n = x[-1]>>16;
	i = (x[-1]>>8)&0xff;
	j = x[-1]&0xff;
}

static void __srandom(unsigned seed) {
	int k;
	uint64_t s = seed;

	if (n == 0) {
		x[0] = s;
		return;
	}
	i = n == 31 || n == 7 ? 3 : 1;
	j = 0;
	for (k = 0; k < n; k++) {
		s = lcg64(s);
		x[k] = s>>32;
	}
	/* make sure x contains at least one odd number */
	x[0] |= 1;
}

void srandom(unsigned seed) {
	LOCK(lock);
	__srandom(seed);
	UNLOCK(lock);
}

char *initstate(unsigned seed, char *state, size_t size) {
	void *old;

	if (size < 8)
		return 0;
	LOCK(lock);
	old = savestate();
	if (size < 32)
		n = 0;
	else if (size < 64)
		n = 7;
	else if (size < 128)
		n = 15;
	else if (size < 256)
		n = 31;
	else
		n = 63;
	x = (uint32_t*)state + 1;
	__srandom(seed);
	savestate();
	UNLOCK(lock);
	return old;
}

char *setstate(char *state) {
	void *old;

	LOCK(lock);
	old = savestate();
	loadstate((uint32_t*)state);
	UNLOCK(lock);
	return old;
}

long random(void) {
	long k;

	LOCK(lock);
	if (n == 0) {
		k = x[0] = lcg31(x[0]);
		goto end;
	}
	x[i] += x[j];
	k = x[i]>>1;
	if (++i == n)
		i = 0;
	if (++j == n)
		j = 0;
end:
	UNLOCK(lock);
	return k;
}


text:
#include <stdlib.h>
#include <inttypes.h>

uint64_t __rand48_step(unsigned short *xi, unsigned short *lc);
extern unsigned short __seed48[7];

long nrand48(unsigned short s[3])
{
	return __rand48_step(s, __seed48+3) >> 17;
}

long lrand48(void)
{
	return nrand48(__seed48);
}


text:
#include <stdlib.h>
#include <string.h>

extern unsigned short __seed48[7];

unsigned short *seed48(unsigned short *s)
{
	static unsigned short p[3];
	memcpy(p, __seed48, sizeof p);
	memcpy(__seed48, s, sizeof p);
	return p;
}


text: unsigned short __seed48[7] = { 0, 0, 0, 0xe66d, 0xdeec, 0x5, 0xb };


text:
#include <stdlib.h>
#include <stdint.h>

static uint64_t seed;

void srand(unsigned s)
{
	seed = s-1;
}

int rand(void)
{
	seed = 6364136223846793005ULL*seed + 1;
	return seed>>33;
}


text:
#include <stdlib.h>
#include <inttypes.h>

uint64_t __rand48_step(unsigned short *xi, unsigned short *lc);
extern unsigned short __seed48[7];

long jrand48(unsigned short s[3])
{
	return __rand48_step(s, __seed48+3) >> 16;
}

long mrand48(void)
{
	return jrand48(__seed48);
}


text:
#include <stdlib.h>

static unsigned temper(unsigned x)
{
	x ^= x>>11;
	x ^= x<<7 & 0x9D2C5680;
	x ^= x<<15 & 0xEFC60000;
	x ^= x>>18;
	return x;
}

int rand_r(unsigned *seed)
{
	return temper(*seed = *seed * 1103515245 + 12345)/2;
}


text:
#include <stdint.h>

uint64_t __rand48_step(unsigned short *xi, unsigned short *lc)
{
	uint64_t a, x;
	x = xi[0] | xi[1]+0U<<16 | xi[2]+0ULL<<32;
	a = lc[0] | lc[1]+0U<<16 | lc[2]+0ULL<<32;
	x = a*x + lc[3];
	xi[0] = x;
	xi[1] = x>>16;
	xi[2] = x>>32;
	return x & 0xffffffffffffull;
}


text:
#include <stdlib.h>
#include <inttypes.h>

uint64_t __rand48_step(unsigned short *xi, unsigned short *lc);
extern unsigned short __seed48[7];

double erand48(unsigned short s[3])
{
	union {
		uint64_t u;
		double f;
	} x = { 0x3ff0000000000000ULL | __rand48_step(s, __seed48+3)<<4 };
	return x.f - 1.0;
}

double drand48(void)
{
	return erand48(__seed48);
}


text:
#include "pwf.h"
#include <pthread.h>

#define FIX(x) (pw->pw_##x = pw->pw_##x-line+buf)

static int getpw_r(const char *name, uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res)
{
	FILE *f;
	char *line = 0;
	size_t len = 0;
	int rv = 0;
	int cs;

	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);

	f = fopen("/etc/passwd", "rbe");
	if (!f) {
		rv = errno;
		goto done;
	}

	*res = 0;
	while (__getpwent_a(f, pw, &line, &len)) {
		if (name && !strcmp(name, pw->pw_name)
		|| !name && pw->pw_uid == uid) {
			if (size < len) {
				rv = ERANGE;
				break;
			}
			*res = pw;
			memcpy(buf, line, len);
			FIX(name);
			FIX(passwd);
			FIX(gecos);
			FIX(dir);
			FIX(shell);
			break;
		}
	}
 	free(line);
	fclose(f);
done:
	pthread_setcancelstate(cs, 0);
	return rv;
}

int getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t size, struct passwd **res)
{
	return getpw_r(name, 0, pw, buf, size, res);
}

int getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res)
{
	return getpw_r(0, uid, pw, buf, size, res);
}


text:
#include <shadow.h>
#include <stdio.h>

#define NUM(n) ((n) == -1 ? 0 : -1), ((n) == -1 ? 0 : (n))
#define STR(s) ((s) ? (s) : "")

int putspent(const struct spwd *sp, FILE *f)
{
	return fprintf(f, "%s:%s:%.*ld:%.*ld:%.*ld:%.*ld:%.*ld:%.*ld:%.*lu\n",
		STR(sp->sp_namp), STR(sp->sp_pwdp), NUM(sp->sp_lstchg),
		NUM(sp->sp_min), NUM(sp->sp_max), NUM(sp->sp_warn),
		NUM(sp->sp_inact), NUM(sp->sp_expire), NUM(sp->sp_flag)) < 0 ? -1 : 0;
}


text:
#include "pwf.h"

static FILE *f;

void setgrent()
{
	if (f) fclose(f);
	f = 0;
}

weak_alias(setgrent, endgrent);

struct group *getgrent()
{
	static char *line, **mem;
	static struct group gr;
	size_t size=0, nmem=0;
	if (!f) f = fopen("/etc/group", "rbe");
	if (!f) return 0;
	return __getgrent_a(f, &gr, &line, &size, &mem, &nmem);
}

struct group *getgrgid(gid_t gid)
{
	struct group *gr;
	int errno_saved;
	setgrent();
	while ((gr=getgrent()) && gr->gr_gid != gid);
	errno_saved = errno;
	endgrent();
	errno = errno_saved;
	return gr;
}

struct group *getgrnam(const char *name)
{
	struct group *gr;
	int errno_saved;
	setgrent();
	while ((gr=getgrent()) && strcmp(gr->gr_name, name));
	errno_saved = errno;
	endgrent();
	errno = errno_saved;
	return gr;
}


text:
#include "pwf.h"

static FILE *f;

void setpwent()
{
	if (f) fclose(f);
	f = 0;
}

weak_alias(setpwent, endpwent);

struct passwd *getpwent()
{
	static char *line;
	static struct passwd pw;
	size_t size=0;
	if (!f) f = fopen("/etc/passwd", "rbe");
	if (!f) return 0;
	return __getpwent_a(f, &pw, &line, &size);
}

struct passwd *getpwuid(uid_t uid)
{
	struct passwd *pw;
	int errno_saved;
	setpwent();
	while ((pw=getpwent()) && pw->pw_uid != uid);
	errno_saved = errno;
	endpwent();
	errno = errno_saved;
	return pw;
}

struct passwd *getpwnam(const char *name)
{
	struct passwd *pw;
	int errno_saved;
	setpwent();
	while ((pw=getpwent()) && strcmp(pw->pw_name, name));
	errno_saved = errno;
	endpwent();
	errno = errno_saved;
	return pw;
}


text:
#include "pwf.h"
#include <pthread.h>

static unsigned atou(char **s)
{
	unsigned x;
	for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0');
	return x;
}

struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size)
{
	ssize_t l;
	char *s;
	int cs;
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
	for (;;) {
		if ((l=getline(line, size, f)) < 0) {
			free(*line);
			*line = 0;
			pw = 0;
			break;
		}
		line[0][l-1] = 0;

		s = line[0];
		pw->pw_name = s++;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; pw->pw_passwd = s;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; pw->pw_uid = atou(&s);
		if (*s != ':') continue;

		*s++ = 0; pw->pw_gid = atou(&s);
		if (*s != ':') continue;

		*s++ = 0; pw->pw_gecos = s;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; pw->pw_dir = s;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; pw->pw_shell = s;
		break;
	}
	pthread_setcancelstate(cs, 0);
	return pw;
}


text:
#define _GNU_SOURCE
#include "pwf.h"

struct passwd *fgetpwent(FILE *f)
{
	static char *line;
	static struct passwd pw;
	size_t size=0;
	return __getpwent_a(f, &pw, &line, &size);
}


text:
#include "pwf.h"

void setspent()
{
}

void endspent()
{
}

struct spwd *getspent()
{
	return 0;
}


text:
#define _GNU_SOURCE
#include <pwd.h>
#include <stdio.h>

int putpwent(const struct passwd *pw, FILE *f)
{
	return fprintf(f, "%s:%s:%d:%d:%s:%s:%s\n",
		pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
		pw->pw_gecos, pw->pw_dir, pw->pw_shell)<0 ? -1 : 0;
}


text:
#include "pwf.h"

#define LINE_LIM 256

struct spwd *getspnam(const char *name)
{
	static struct spwd sp;
	static char *line;
	struct spwd *res;
	int e;

	if (!line) line = malloc(LINE_LIM);
	if (!line) return 0;
	e = getspnam_r(name, &sp, line, LINE_LIM, &res);
	if (e) errno = e;
	return res;
}


text:
#define _GNU_SOURCE
#include <grp.h>
#include <stdio.h>

int putgrent(const struct group *gr, FILE *f)
{
	int r;
	size_t i;
	flockfile(f);
	if ((r = fprintf(f, "%s:%s:%d:", gr->gr_name, gr->gr_passwd, gr->gr_gid))<0) goto done;
	if (gr->gr_mem) for (i=0; gr->gr_mem[i]; i++)
		if ((r = fprintf(f, "%s%s", i?",":"", gr->gr_mem[i]))<0) goto done;
	r = fputc('\n', f);
done:
	funlockfile(f);
	return r<0 ? -1 : 0;
}


text:
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#include <pthread.h>
#include "pwf.h"

/* This implementation support Openwall-style TCB passwords in place of
 * traditional shadow, if the appropriate directories and files exist.
 * Thus, it is careful to avoid following symlinks or blocking on fifos
 * which a malicious user might create in place of his or her TCB shadow
 * file. It also avoids any allocation to prevent memory-exhaustion
 * attacks via huge TCB shadow files. */

static long xatol(char **s)
{
	long x;
	if (**s == ':' || **s == '\n') return -1;
	for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0');
	return x;
}

int __parsespent(char *s, struct spwd *sp)
{
	sp->sp_namp = s;
	if (!(s = strchr(s, ':'))) return -1;
	*s = 0;

	sp->sp_pwdp = ++s;
	if (!(s = strchr(s, ':'))) return -1;
	*s = 0;

	s++; sp->sp_lstchg = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_min = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_max = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_warn = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_inact = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_expire = xatol(&s);
	if (*s != ':') return -1;

	s++; sp->sp_flag = xatol(&s);
	if (*s != '\n') return -1;
	return 0;
}

static void cleanup(void *p)
{
	fclose(p);
}

int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res)
{
	char path[20+NAME_MAX];
	FILE *f = 0;
	int rv = 0;
	int fd;
	size_t k, l = strlen(name);
	int skip = 0;
	int cs;

	*res = 0;

	/* Disallow potentially-malicious user names */
	if (*name=='.' || strchr(name, '/') || !l)
		return EINVAL;

	/* Buffer size must at least be able to hold name, plus some.. */
	if (size < l+100) return ERANGE;

	/* Protect against truncation */
	if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path)
		return EINVAL;

	fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
	if (fd >= 0) {
		struct stat st = { 0 };
		errno = EINVAL;
		if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
			close(fd);
			pthread_setcancelstate(cs, 0);
			return errno;
		}
	} else {
		f = fopen("/etc/shadow", "rbe");
		if (!f) return errno;
	}

	pthread_cleanup_push(cleanup, f);
	while (fgets(buf, size, f) && (k=strlen(buf))>0) {
		if (skip || strncmp(name, buf, l)) {
			skip = buf[k-1] != '\n';
			continue;
		}
		if (buf[k-1] != '\n') {
			rv = ERANGE;
			break;
		}

		if (__parsespent(buf, sp) < 0) continue;
		*res = sp;
		break;
	}
	pthread_cleanup_pop(1);
	return rv;
}


text:
#include "pwf.h"
#include <pthread.h>

static unsigned atou(char **s)
{
	unsigned x;
	for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0');
	return x;
}

struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem)
{
	ssize_t l;
	char *s, *mems;
	size_t i;
	int cs;
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
	for (;;) {
		if ((l=getline(line, size, f)) < 0) {
			free(*line);
			*line = 0;
			gr = 0;
			goto end;
		}
		line[0][l-1] = 0;

		s = line[0];
		gr->gr_name = s++;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; gr->gr_passwd = s;
		if (!(s = strchr(s, ':'))) continue;

		*s++ = 0; gr->gr_gid = atou(&s);
		if (*s != ':') continue;

		*s++ = 0; mems = s;
		break;
	}

	for (*nmem=!!*s; *s; s++)
		if (*s==',') ++*nmem;
	free(*mem);
	*mem = calloc(sizeof(char *), *nmem+1);
	if (!*mem) {
		free(*line);
		*line = 0;
		return 0;
	}
	if (*mems) {
		mem[0][0] = mems;
		for (s=mems, i=0; *s; s++)
			if (*s==',') *s++ = 0, mem[0][++i] = s;
		mem[0][++i] = 0;
	} else {
		mem[0][0] = 0;
	}
	gr->gr_mem = *mem;
end:
	pthread_setcancelstate(cs, 0);
	return gr;
}


text:
#include <shadow.h>

int lckpwdf()
{
	return 0;
}

int ulckpwdf()
{
	return 0;
}


text:
#include "pwf.h"
#include <pthread.h>

struct spwd *fgetspent(FILE *f)
{
	static char *line;
	static struct spwd sp;
	size_t size = 0;
	struct spwd *res = 0;
	int cs;
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
	if (getline(&line, &size, f) >= 0 && __parsespent(line, &sp) >= 0) res = &sp;
	pthread_setcancelstate(cs, 0);
	return res;
}


text:
#include <pwd.h>
#include <grp.h>
#include <shadow.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include "libc.h"

struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size);
struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem);
int __parsespent(char *s, struct spwd *sp);


text:
#include "pwf.h"
#include <pthread.h>

#define FIX(x) (gr->gr_##x = gr->gr_##x-line+buf)

static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf, size_t size, struct group **res)
{
	FILE *f;
	char *line = 0;
	size_t len = 0;
	char **mem = 0;
	size_t nmem = 0;
	int rv = 0;
	size_t i;
	int cs;

	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);

	f = fopen("/etc/group", "rbe");
	if (!f) {
		rv = errno;
		goto done;
	}

	*res = 0;
	while (__getgrent_a(f, gr, &line, &len, &mem, &nmem)) {
		if (name && !strcmp(name, gr->gr_name)
		|| !name && gr->gr_gid == gid) {
			if (size < len + (nmem+1)*sizeof(char *) + 32) {
				rv = ERANGE;
				break;
			}
			*res = gr;
			buf += (16-(uintptr_t)buf)%16;
			gr->gr_mem = (void *)buf;
			buf += (nmem+1)*sizeof(char *);
			memcpy(buf, line, len);
			FIX(name);
			FIX(passwd);
			for (i=0; mem[i]; i++)
				gr->gr_mem[i] = mem[i]-line+buf;
			gr->gr_mem[i] = 0;
			break;
		}
	}
 	free(mem);
 	free(line);
	fclose(f);
done:
	pthread_setcancelstate(cs, 0);
	return rv;
}

int getgrnam_r(const char *name, struct group *gr, char *buf, size_t size, struct group **res)
{
	return getgr_r(name, 0, gr, buf, size, res);
}

int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res)
{
	return getgr_r(0, gid, gr, buf, size, res);
}


text:
#define _GNU_SOURCE
#include "pwf.h"

struct group *fgetgrent(FILE *f)
{
	static char *line, **mem;
	static struct group gr;
	size_t size=0, nmem=0;
	return __getgrent_a(f, &gr, &line, &size, &mem, &nmem);
}


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include <errno.h>
#include "syscall.h"

int sync_file_range(int fd, off_t pos, off_t len, unsigned flags)
{
#if defined(SYS_sync_file_range2)
	return syscall(SYS_sync_file_range2, fd, flags,
		__SYSCALL_LL_E(pos), __SYSCALL_LL_E(len));
#elif defined(SYS_sync_file_range)
	return syscall(SYS_sync_file_range, fd,
		__SYSCALL_LL_O(pos), __SYSCALL_LL_E(len), flags);
#else
	return __syscall_ret(-ENOSYS);
#endif
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "syscall.h"

int chroot(const char *path)
{
	return syscall(SYS_chroot, path);
}


text:
#define _GNU_SOURCE
#include <time.h>
#include <sys/time.h>

int stime(const time_t *t)
{
	struct timeval tv = { .tv_sec = *t, .tv_usec = 0 };
	return settimeofday(&tv, (void *)0);
}


text:
#include <sys/reboot.h>
#include "syscall.h"

int reboot(int type)
{
	return syscall(SYS_reboot, 0xfee1dead, 672274793, type);
}


text:
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"

int unshare(int flags)
{
	return syscall(SYS_unshare, flags);
}


text:
#include "syscall.h"
#ifdef SYS_arch_prctl
int arch_prctl(int code, unsigned long addr)
{
	return syscall(SYS_arch_prctl, code, addr);
}
#endif


text:
#include <sys/fsuid.h>
#include "syscall.h"
#include "libc.h"

int setfsgid(gid_t gid)
{
	return syscall(SYS_setfsgid, gid);
}


text:
#include "syscall.h"
#include "libc.h"

#ifdef SYS_cacheflush
int _flush_cache(void *addr, int len, int op)
{
	return syscall(SYS_cacheflush, addr, len, op);
}
weak_alias(_flush_cache, cacheflush);
#endif

#ifdef SYS_cachectl
int __cachectl(void *addr, int len, int op)
{
	return syscall(SYS_cachectl, addr, len, op);
}
weak_alias(__cachectl, cachectl);
#endif


text:
#define _GNU_SOURCE
#include <sys/mman.h>
#include "syscall.h"

int remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, int flags)
{
	return syscall(SYS_remap_file_pages, addr, size, prot, pgoff, flags);
}


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include "syscall.h"

ssize_t vmsplice(int fd, const struct iovec *iov, size_t cnt, unsigned flags)
{
	return syscall(SYS_vmsplice, fd, iov, cnt, flags);
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "syscall.h"

int sethostname(const char *name, size_t len)
{
	return syscall(SYS_sethostname, name, len);
}


text:
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"

int setns(int fd, int nstype)
{
	return syscall(SYS_setns, fd, nstype);
}


text:
#include "syscall.h"

#ifdef SYS_ioperm
#include <sys/io.h>

int ioperm(unsigned long from, unsigned long num, int turn_on)
{
	return syscall(SYS_ioperm, from, num, turn_on);
}
#endif


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include "syscall.h"

ssize_t tee(int src, int dest, size_t len, unsigned flags)
{
	return syscall(SYS_tee, src, dest, len, flags);
}


text:
#include <sys/timex.h>
#include "syscall.h"

int clock_adjtime (clockid_t clock_id, struct timex *utx)
{
	return syscall(SYS_clock_adjtime, clock_id, utx);
}


text:
#include <sys/prctl.h>
#include <stdarg.h>
#include "syscall.h"

int prctl(int op, ...)
{
	unsigned long x[4];
	int i;
	va_list ap;
	va_start(ap, op);
	for (i=0; i<4; i++) x[i] = va_arg(ap, unsigned long);
	va_end(ap);
	return syscall(SYS_prctl, op, x[0], x[1], x[2], x[3]);
}


text:
#include <sys/file.h>
#include "syscall.h"

int flock(int fd, int op)
{
	return syscall(SYS_flock, fd, op);
}


text:
#include <sys/klog.h>
#include "syscall.h"

int klogctl (int type, char *buf, int len)
{
	return syscall(SYS_syslog, type, buf, len);
}


text:
#include <sys/timerfd.h>
#include "syscall.h"

int timerfd_create(int clockid, int flags)
{
	return syscall(SYS_timerfd_create, clockid, flags);
}

int timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old)
{
	return syscall(SYS_timerfd_settime, fd, flags, new, old);
}

int timerfd_gettime(int fd, struct itimerspec *cur)
{
	return syscall(SYS_timerfd_gettime, fd, cur);
}


text:
#include <sys/quota.h>
#include "syscall.h"

int quotactl(int cmd, const char *special, int id, char *addr)
{
	return syscall(SYS_quotactl, cmd, special, id, addr);
}


text:
#include <sys/time.h>
#include "fcntl.h"
#include "syscall.h"

int __futimesat(int, const char *, const struct timeval [2]);

int utimes(const char *path, const struct timeval times[2])
{
	return __futimesat(AT_FDCWD, path, times);
}


text:
#include <sys/timex.h>
#include "syscall.h"

int adjtimex(struct timex *tx)
{
	return syscall(SYS_adjtimex, tx);
}


text:
#include <sys/signalfd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include "syscall.h"

int signalfd(int fd, const sigset_t *sigs, int flags)
{
	int ret = __syscall(SYS_signalfd4, fd, sigs, _NSIG/8, flags);
#ifdef SYS_signalfd
	if (ret != -ENOSYS) return __syscall_ret(ret);
	ret = __syscall(SYS_signalfd, fd, sigs, _NSIG/8);
	if (ret >= 0) {
		if (flags & SFD_CLOEXEC)
			__syscall(SYS_fcntl, ret, F_SETFD, FD_CLOEXEC);
		if (flags & SFD_NONBLOCK)
			__syscall(SYS_fcntl, ret, F_SETFL, O_NONBLOCK);
	}
#endif
	return __syscall_ret(ret);
}


text:
#include <sys/sysinfo.h>
#include "syscall.h"
#include "libc.h"

int __lsysinfo(struct sysinfo *info)
{
	return syscall(SYS_sysinfo, info);
}

weak_alias(__lsysinfo, sysinfo);


text:
#define _GNU_SOURCE
#include <sys/resource.h>
#include "syscall.h"
#include "libc.h"

#define FIX(x) do{ if ((x)>=SYSCALL_RLIM_INFINITY) (x)=RLIM_INFINITY; }while(0)

int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit)
{
	struct rlimit tmp;
	int r;
	if (new_limit && SYSCALL_RLIM_INFINITY != RLIM_INFINITY) {
		tmp = *new_limit;
		FIX(tmp.rlim_cur);
		FIX(tmp.rlim_max);
		new_limit = &tmp;
	}
	r = syscall(SYS_prlimit64, pid, resource, new_limit, old_limit);
	if (!r && old_limit && SYSCALL_RLIM_INFINITY != RLIM_INFINITY) {
		FIX(old_limit->rlim_cur);
		FIX(old_limit->rlim_max);
	}
	return r;
}

#undef prlimit64
LFS64(prlimit);


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include "syscall.h"

ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned flags)
{
	return syscall(SYS_splice, fd_in, off_in, fd_out, off_out, len, flags);
}


text:
#include "syscall.h"

int pivot_root(const char *new, const char *old)
{
	return syscall(SYS_pivot_root, new, old);
}


text:
#define _GNU_SOURCE
#include <sys/uio.h>
#include "syscall.h"

ssize_t process_vm_writev(pid_t pid, const struct iovec *lvec, unsigned long liovcnt, const struct iovec *rvec, unsigned long riovcnt, unsigned long flags)
{
	return syscall(SYS_process_vm_writev, pid, lvec, liovcnt, rvec, riovcnt, flags);
}

ssize_t process_vm_readv(pid_t pid, const struct iovec *lvec, unsigned long liovcnt, const struct iovec *rvec, unsigned long riovcnt, unsigned long flags)
{
	return syscall(SYS_process_vm_readv, pid, lvec, liovcnt, rvec, riovcnt, flags);
}


text:
#include <sys/eventfd.h>
#include <unistd.h>
#include <errno.h>
#include "syscall.h"

int eventfd(unsigned int count, int flags)
{
	int r = __syscall(SYS_eventfd2, count, flags);
#ifdef SYS_eventfd
	if (r==-ENOSYS && !flags) r = __syscall(SYS_eventfd, count);
#endif
	return __syscall_ret(r);
}

int eventfd_read(int fd, eventfd_t *value)
{
	return (sizeof(*value) == read(fd, value, sizeof(*value))) ? 0 : -1;
}

int eventfd_write(int fd, eventfd_t value)
{
	return (sizeof(value) == write(fd, &value, sizeof(value))) ? 0 : -1;
}


text:
#include "syscall.h"

int capset(void *a, void *b)
{
	return syscall(SYS_capset, a, b);
}

int capget(void *a, void *b)
{
	return syscall(SYS_capget, a, b);
}


text:
#include <sys/sendfile.h>
#include "syscall.h"
#include "libc.h"

ssize_t sendfile(int out_fd, int in_fd, off_t *ofs, size_t count)
{
	return syscall(SYS_sendfile, out_fd, in_fd, ofs, count);
}

LFS64(sendfile);


text:
#define _GNU_SOURCE
#include <sys/time.h>
#include <sys/timex.h>
#include <errno.h>
#include "syscall.h"

int adjtime(const struct timeval *in, struct timeval *out)
{
	struct timex tx = { 0 };
	if (in) {
		if (in->tv_sec > 1000 || in->tv_usec > 1000000000) {
			errno = EINVAL;
			return -1;
		}
		tx.offset = in->tv_sec*1000000 + in->tv_usec;
		tx.modes = ADJ_OFFSET_SINGLESHOT;
	}
	if (syscall(SYS_adjtimex, &tx) < 0) return -1;
	if (out) {
		out->tv_sec = tx.offset / 1000000;
		if ((out->tv_usec = tx.offset % 1000000) < 0) {
			out->tv_sec--;
			out->tv_usec += 1000000;
		}
	}
	return 0;
}


text:
#include <sys/ptrace.h>
#include <stdarg.h>
#include <unistd.h>
#include "syscall.h"

long ptrace(int req, ...)
{
	va_list ap;
	pid_t pid;
	void *addr, *data, *addr2;
	long ret, result;

	va_start(ap, req);
	pid = va_arg(ap, pid_t);
	addr = va_arg(ap, void *);
	data = va_arg(ap, void *);
	addr2 = va_arg(ap, void *);
	va_end(ap);

	if (req-1U < 3) data = &result;
	ret = syscall(SYS_ptrace, req, pid, addr, data, addr2);

	if (ret < 0 || req-1U >= 3) return ret;
	return result;
}


text:
#include <errno.h>
#include "syscall.h"

int brk(void *end)
{
	return __syscall_ret(-ENOMEM);
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "syscall.h"

int vhangup(void)
{
	return syscall(SYS_vhangup);
}


text:
#include <sys/personality.h>
#include "syscall.h"
#ifdef SYS_personality
int personality(unsigned long persona)
{
	return syscall(SYS_personality, persona);
}
#endif


text:
#include "syscall.h"
#include <sys/fanotify.h>

int fanotify_init(unsigned flags, unsigned event_f_flags)
{
	return syscall(SYS_fanotify_init, flags, event_f_flags);
}

int fanotify_mark(int fanotify_fd, unsigned flags, unsigned long long mask,
	          int dfd, const char *pathname)
{
	return syscall(SYS_fanotify_mark, fanotify_fd, flags, __SYSCALL_LL_E(mask), dfd, pathname);
}


text:
#include <sys/fsuid.h>
#include "syscall.h"
#include "libc.h"

int setfsuid(uid_t uid)
{
	return syscall(SYS_setfsuid, uid);
}


text:
#include <sys/mount.h>
#include "syscall.h"

int mount(const char *special, const char *dir, const char *fstype, unsigned long flags, const void *data)
{
	return syscall(SYS_mount, special, dir, fstype, flags, data);
}

int umount(const char *special)
{
	return syscall(SYS_umount2, special, 0);
}

int umount2(const char *special, int flags)
{
	return syscall(SYS_umount2, special, flags);
}


text:
#define _GNU_SOURCE
#include <sys/wait.h>
#include <sys/resource.h>
#include "syscall.h"

pid_t wait3(int *status, int options, struct rusage *usage)
{
	return wait4(-1, status, options, usage);
}


text:
#define _BSD_SOURCE
#include <sys/time.h>
#include "syscall.h"

int settimeofday(const struct timeval *tv, const struct timezone *tz)
{
	return syscall(SYS_settimeofday, tv, 0);
}


text:
#define _GNU_SOURCE
#include <poll.h>
#include <signal.h>
#include "syscall.h"

int ppoll(struct pollfd *fds, nfds_t n, const struct timespec *to, const sigset_t *mask)
{
	return syscall_cp(SYS_ppoll, fds, n,
		to ? (struct timespec []){*to} : 0, mask, _NSIG/8);
}


text:
#include <sys/epoll.h>
#include <signal.h>
#include <errno.h>
#include "syscall.h"

int epoll_create(int size)
{
	return epoll_create1(0);
}

int epoll_create1(int flags)
{
	int r = __syscall(SYS_epoll_create1, flags);
#ifdef SYS_epoll_create
	if (r==-ENOSYS && !flags) r = __syscall(SYS_epoll_create, 1);
#endif
	return __syscall_ret(r);
}

int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
{
	return syscall(SYS_epoll_ctl, fd, op, fd2, ev);
}

int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs)
{
	int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8);
#ifdef SYS_epoll_wait
	if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to);
#endif
	return __syscall_ret(r);
}

int epoll_wait(int fd, struct epoll_event *ev, int cnt, int to)
{
	return epoll_pwait(fd, ev, cnt, to, 0);
}


text:
#include "syscall.h"

int init_module(void *a, unsigned long b, const char *c)
{
	return syscall(SYS_init_module, a, b, c);
}

int delete_module(const char *a, unsigned b)
{
	return syscall(SYS_delete_module, a, b);
}


text:
#include <stdint.h>
#include <errno.h>
#include "syscall.h"

void *sbrk(intptr_t inc)
{
	if (inc) return (void *)__syscall_ret(-ENOMEM);
	return (void *)__syscall(SYS_brk, 0);
}


text:
#include <sys/swap.h>
#include "syscall.h"

int swapon(const char *path, int flags)
{
	return syscall(SYS_swapon, path, flags);
}

int swapoff(const char *path)
{
	return syscall(SYS_swapoff, path);
}


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "syscall.h"

int setgroups(size_t count, const gid_t list[])
{
	return syscall(SYS_setgroups, count, list);
}


text:
#include <sys/inotify.h>
#include <errno.h>
#include "syscall.h"

int inotify_init()
{
	return inotify_init1(0);
}
int inotify_init1(int flags)
{
	int r = __syscall(SYS_inotify_init1, flags);
#ifdef SYS_inotify_init
	if (r==-ENOSYS && !flags) r = __syscall(SYS_inotify_init);
#endif
	return __syscall_ret(r);
}

int inotify_add_watch(int fd, const char *pathname, uint32_t mask)
{
	return syscall(SYS_inotify_add_watch, fd, pathname, mask);
}

int inotify_rm_watch(int fd, int wd)
{
	return syscall(SYS_inotify_rm_watch, fd, wd);
}


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include "syscall.h"
#include "libc.h"

int fallocate(int fd, int mode, off_t base, off_t len)
{
	return syscall(SYS_fallocate, fd, mode, __SYSCALL_LL_E(base),
		__SYSCALL_LL_E(len));
}

#undef fallocate64
LFS64(fallocate);


text:
#include <stdarg.h>
#include <unistd.h>
#include "pthread_impl.h"
#include "syscall.h"

int clone(int (*func)(void *), void *stack, int flags, void *arg, ...)
{
	va_list ap;
	pid_t *ptid, *ctid;
	void  *tls;

	va_start(ap, arg);
	ptid = va_arg(ap, pid_t *);
	tls  = va_arg(ap, void *);
	ctid = va_arg(ap, pid_t *);
	va_end(ap);

	return __syscall_ret(__clone(func, stack, flags, arg, ptid, tls, ctid));
}


text:
#include "syscall.h"

#ifdef SYS_iopl
#include <sys/io.h>

int iopl(int level)
{
	return syscall(SYS_iopl, level);
}
#endif


text:
#define _GNU_SOURCE
#include <unistd.h>
#include "syscall.h"

void syncfs(int fd)
{
	__syscall(SYS_syncfs, fd);
}


text:
#include <sys/xattr.h>
#include "syscall.h"

ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
{
	return syscall(SYS_getxattr, path, name, value, size);
}

ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size)
{
	return syscall(SYS_lgetxattr, path, name, value, size);
}

ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size)
{
	return syscall(SYS_fgetxattr, filedes, name, value, size);
}

ssize_t listxattr(const char *path, char *list, size_t size)
{
	return syscall(SYS_listxattr, path, list, size);
}

ssize_t llistxattr(const char *path, char *list, size_t size)
{
	return syscall(SYS_llistxattr, path, list, size);
}

ssize_t flistxattr(int filedes, char *list, size_t size)
{
	return syscall(SYS_flistxattr, filedes, list, size);
}

int setxattr(const char *path, const char *name, const void *value, size_t size, int flags)
{
	return syscall(SYS_setxattr, path, name, value, size, flags);
}

int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags)
{
	return syscall(SYS_lsetxattr, path, name, value, size, flags);
}

int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags)
{
	return syscall(SYS_fsetxattr, filedes, name, value, size, flags);
}

int removexattr(const char *path, const char *name)
{
	return syscall(SYS_removexattr, path, name);
}

int lremovexattr(const char *path, const char *name)
{
	return syscall(SYS_lremovexattr, path, name);
}

int fremovexattr(int fd, const char *name)
{
	return syscall(SYS_fremovexattr, fd, name);
}


text:
#define _GNU_SOURCE
#include <sys/wait.h>
#include <sys/resource.h>
#include "syscall.h"

pid_t wait4(pid_t pid, int *status, int options, struct rusage *usage)
{
	return syscall(SYS_wait4, pid, status, options, usage);
}


text:
#define _GNU_SOURCE
#include <fcntl.h>
#include "syscall.h"

ssize_t readahead(int fd, off_t pos, size_t len)
{
	return syscall(SYS_readahead, fd, __SYSCALL_LL_O(pos), len);
}


text: # see arch/x32/src/sysinfo.c


text:
#include <sys/select.h>
#include <signal.h>
#include <stdint.h>
#include "syscall.h"
#include "libc.h"

int pselect(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, const struct timespec *restrict ts, const sigset_t *restrict mask)
{
	syscall_arg_t data[2] = { (uintptr_t)mask, _NSIG/8 };
	struct timespec ts_tmp;
	if (ts) ts_tmp = *ts;
	return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, ts ? &ts_tmp : 0, data);
}


text:
#include <sys/select.h>
#include <signal.h>
#include <stdint.h>
#include <errno.h>
#include "syscall.h"
#include "libc.h"

int select(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, struct timeval *restrict tv)
{
#ifdef SYS_select
	return syscall_cp(SYS_select, n, rfds, wfds, efds, tv);
#else
	syscall_arg_t data[2] = { 0, _NSIG/8 };
	struct timespec ts;
	if (tv) {
		if (tv->tv_sec < 0 || tv->tv_usec < 0)
			return __syscall_ret(-EINVAL);
		time_t extra_secs = tv->tv_usec / 1000000;
		ts.tv_nsec = tv->tv_usec % 1000000 * 1000;
		const time_t max_time = (1ULL<<8*sizeof(time_t)-1)-1;
		ts.tv_sec = extra_secs > max_time - tv->tv_sec ?
			max_time : tv->tv_sec + extra_secs;
	}
	return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, tv ? &ts : 0, data);
#endif
}


text:
#include <poll.h>
#include <time.h>
#include <signal.h>
#include "syscall.h"
#include "libc.h"

int poll(struct pollfd *fds, nfds_t n, int timeout)
{
#ifdef SYS_poll
	return syscall_cp(SYS_poll, fds, n, timeout);
#else
	return syscall_cp(SYS_ppoll, fds, n, timeout>=0 ?
		&((struct timespec){ .tv_sec = timeout/1000,
		.tv_nsec = timeout%1000*1000000 }) : 0, 0, _NSIG/8);
#endif
}


text:
#include "pthread_impl.h"
#include <stddef.h>

void __do_private_robust_list()
{
	pthread_t self = __pthread_self();
	volatile void *volatile *p;
	volatile void *volatile *prev;
	volatile void *volatile *next;
	pthread_mutex_t *m;

	prev = &self->robust_list.head;
	for (p=self->robust_list.head; p&&p!=&self->robust_list.head; p=next) {
		next = *p;
		m = (void *)((char *)p - offsetof(pthread_mutex_t, _m_next));
		if (!(m->_m_type & 128)) {
			int waiters = m->_m_waiters;
			*prev = next;
			int cont = a_swap(&m->_m_lock, self->tid|0x40000000);
			if (cont < 0 || waiters) __wake(&m->_m_lock, 1, 1);
		} else {
			prev = p;
		}
	}
}

int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int robust)
{
	if (robust > 1U) return EINVAL;
	a->__attr &= ~4;
	a->__attr |= robust*4;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_mutex_consistent(pthread_mutex_t *m)
{
	if (!(m->_m_type & 8)) return EINVAL;
	if ((m->_m_lock & 0x7fffffff) != __pthread_self()->tid)
		return EPERM;
	m->_m_type &= ~8U;
	return 0;
}


text:
#include <threads.h>

int __pthread_key_delete(tss_t k);

void tss_delete(tss_t key)
{
	__pthread_key_delete(key);
}


text:
#include "pthread_impl.h"

int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
{
	if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
	    && !a_cas(&m->_m_lock, 0, EBUSY))
		return 0;

	int r, t, priv = (m->_m_type & 128) ^ 128;

	r = pthread_mutex_trylock(m);
	if (r != EBUSY) return r;

	int spins = 100;
	while (spins-- && m->_m_lock && !m->_m_waiters) a_spin();

	while ((r=pthread_mutex_trylock(m)) == EBUSY) {
		if (!(r=m->_m_lock) || ((r&0x40000000) && (m->_m_type&4)))
			continue;
		if ((m->_m_type&3) == PTHREAD_MUTEX_ERRORCHECK
		 && (r&0x7fffffff) == __pthread_self()->tid)
			return EDEADLK;

		a_inc(&m->_m_waiters);
		t = r | 0x80000000;
		a_cas(&m->_m_lock, r, t);
		r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, 0, 0, priv);
		a_dec(&m->_m_waiters);
		if (r && r != EINTR) break;
	}
	return r;
}

weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock);


text:
#include "pthread_impl.h"

int pthread_setschedprio(pthread_t t, int prio)
{
	int r;
	__lock(t->killlock);
	r = t->dead ? ESRCH : -__syscall(SYS_sched_setparam, t->tid, &prio);
	__unlock(t->killlock);
	return r;
}


text:
#include <pthread.h>
#include "libc.h"

static struct atfork_funcs {
	void (*prepare)(void);
	void (*parent)(void);
	void (*child)(void);
	struct atfork_funcs *prev, *next;
} *funcs;

static int lock[2];

void __fork_handler(int who)
{
	struct atfork_funcs *p;
	if (!funcs) return;
	if (who < 0) {
		LOCK(lock);
		for (p=funcs; p; p = p->next) {
			if (p->prepare) p->prepare();
			funcs = p;
		}
	} else {
		for (p=funcs; p; p = p->prev) {
			if (!who && p->parent) p->parent();
			else if (who && p->child) p->child();
			funcs = p;
		}
		UNLOCK(lock);
	}
}

int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
	struct atfork_funcs *new = malloc(sizeof *new);
	if (!new) return -1;

	LOCK(lock);
	new->next = funcs;
	new->prev = 0;
	new->prepare = prepare;
	new->parent = parent;
	new->child = child;
	if (funcs) funcs->prev = new;
	funcs = new;
	UNLOCK(lock);
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_condattr_destroy(pthread_condattr_t *a)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
{
	int r, t;

	r = pthread_rwlock_trywrlock(rw);
	if (r != EBUSY) return r;

	int spins = 100;
	while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();

	while ((r=pthread_rwlock_trywrlock(rw))==EBUSY) {
		if (!(r=rw->_rw_lock)) continue;
		t = r | 0x80000000;
		a_inc(&rw->_rw_waiters);
		a_cas(&rw->_rw_lock, r, t);
		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, rw->_rw_shared^128);
		a_dec(&rw->_rw_waiters);
		if (r && r != EINTR) return r;
	}
	return r;
}


text:
#include <semaphore.h>
#include <sys/mman.h>

int sem_unlink(const char *name)
{
	return shm_unlink(name);
}


text:
#include "pthread_impl.h"

int pthread_attr_setschedpolicy(pthread_attr_t *a, int policy)
{
	a->_a_policy = policy;
	return 0;
}


text:
#include "pthread_impl.h"

static void undo(void *control)
{
	a_store(control, 0);
	__wake(control, 1, 1);
}

int __pthread_once(pthread_once_t *control, void (*init)(void))
{
	static int waiters;

	/* Return immediately if init finished before, but ensure that
	 * effects of the init routine are visible to the caller. */
	if (*control == 2) {
		a_barrier();
		return 0;
	}

	/* Try to enter initializing state. Three possibilities:
	 *  0 - we're the first or the other cancelled; run init
	 *  1 - another thread is running init; wait
	 *  2 - another thread finished running init; just return */

	for (;;) switch (a_cas(control, 0, 1)) {
	case 0:
		pthread_cleanup_push(undo, control);
		init();
		pthread_cleanup_pop(0);

		a_store(control, 2);
		if (waiters) __wake(control, -1, 1);
		return 0;
	case 1:
		__wait(control, &waiters, 1, 1);
		continue;
	case 2:
		return 0;
	}
}

weak_alias(__pthread_once, pthread_once);


text:
#include "pthread_impl.h"

int pthread_barrier_init(pthread_barrier_t *restrict b, const pthread_barrierattr_t *restrict a, unsigned count)
{
	if (count-1 > INT_MAX-1) return EINVAL;
	*b = (pthread_barrier_t){ ._b_limit = count-1 | (a?a->__attr:0) };
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int protocol)
{
	if (protocol) return ENOTSUP;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type)
{
	if ((unsigned)type > 2) return EINVAL;
	a->__attr = (a->__attr & ~3) | type;
	return 0;
}


text:
#include <threads.h>
#include <errno.h>

int __pthread_mutex_timedlock(mtx_t *restrict, const struct timespec *restrict);

int mtx_timedlock(mtx_t *restrict m, const struct timespec *restrict ts)
{
	int ret = __pthread_mutex_timedlock(m, ts);
	switch (ret) {
	default:        return thrd_error;
	case 0:         return thrd_success;
	case ETIMEDOUT: return thrd_timedout;
	}
}


text:
#include <semaphore.h>
#include "pthread_impl.h"

int sem_post(sem_t *sem)
{
	int val, waiters, priv = sem->__val[2];
	do {
		val = sem->__val[0];
		waiters = sem->__val[1];
		if (val == SEM_VALUE_MAX) {
			errno = EOVERFLOW;
			return -1;
		}
	} while (a_cas(sem->__val, val, val+1+(val<0)) != val);
	if (val<0 || waiters) __wake(sem->__val, 1, priv);
	return 0;
}


text:
#include <semaphore.h>
#include "pthread_impl.h"

int sem_trywait(sem_t *sem)
{
	int val;
	while ((val=sem->__val[0]) > 0) {
		int new = val-1-(val==1 && sem->__val[1]);
		if (a_cas(sem->__val, val, new)==val) return 0;
	}
	errno = EAGAIN;
	return -1;
}


text:
#include "pthread_impl.h"

int pthread_setspecific(pthread_key_t k, const void *x)
{
	struct pthread *self = __pthread_self();
	/* Avoid unnecessary COW */
	if (self->tsd[k] != x) {
		self->tsd[k] = (void *)x;
		self->tsd_used = 1;
	}
	return 0;
}


text:
#include <pthread.h>
#include <errno.h>

int pthread_setconcurrency(int val)
{
	if (val < 0) return EINVAL;
	if (val > 0) return EAGAIN;
	return 0;
}


text:
#include <semaphore.h>

int sem_wait(sem_t *sem)
{
	return sem_timedwait(sem, 0);
}


text:
#include "pthread_impl.h"

int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_attr_getdetachstate(const pthread_attr_t *a, int *state)
{
	*state = a->_a_detach;
	return 0;
}
int pthread_attr_getguardsize(const pthread_attr_t *restrict a, size_t *restrict size)
{
	*size = a->_a_guardsize + DEFAULT_GUARD_SIZE;
	return 0;
}

int pthread_attr_getinheritsched(const pthread_attr_t *restrict a, int *restrict inherit)
{
	*inherit = a->_a_sched;
	return 0;
}

int pthread_attr_getschedparam(const pthread_attr_t *restrict a, struct sched_param *restrict param)
{
	param->sched_priority = a->_a_prio;
	return 0;
}

int pthread_attr_getschedpolicy(const pthread_attr_t *restrict a, int *restrict policy)
{
	*policy = a->_a_policy;
	return 0;
}

int pthread_attr_getscope(const pthread_attr_t *restrict a, int *restrict scope)
{
	*scope = PTHREAD_SCOPE_SYSTEM;
	return 0;
}

int pthread_attr_getstack(const pthread_attr_t *restrict a, void **restrict addr, size_t *restrict size)
{
	if (!a->_a_stackaddr)
		return EINVAL;
	*size = a->_a_stacksize + DEFAULT_STACK_SIZE;
	*addr = (void *)(a->_a_stackaddr - *size);
	return 0;
}

int pthread_attr_getstacksize(const pthread_attr_t *restrict a, size_t *restrict size)
{
	*size = a->_a_stacksize + DEFAULT_STACK_SIZE;
	return 0;
}

int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict a, int *restrict pshared)
{
	*pshared = !!a->__attr;
	return 0;
}

int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *restrict clk)
{
	*clk = a->__attr & 0x7fffffff;
	return 0;
}

int pthread_condattr_getpshared(const pthread_condattr_t *restrict a, int *restrict pshared)
{
	*pshared = a->__attr>>31;
	return 0;
}

int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict a, int *restrict protocol)
{
	*protocol = PTHREAD_PRIO_NONE;
	return 0;
}
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict a, int *restrict pshared)
{
	*pshared = a->__attr / 128U % 2;
	return 0;
}

int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict a, int *restrict robust)
{
	*robust = a->__attr / 4U % 2;
	return 0;
}

int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict a, int *restrict type)
{
	*type = a->__attr & 3;
	return 0;
}

int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict a, int *restrict pshared)
{
	*pshared = a->__attr[0];
	return 0;
}


text:
#include <threads.h>

int __pthread_mutex_unlock(mtx_t *);

int mtx_unlock(mtx_t *mtx)
{
	/* The only cases where pthread_mutex_unlock can return an
	 * error are undefined behavior for C11 mtx_unlock, so we can
	 * assume it does not return an error and simply tail call. */
	return __pthread_mutex_unlock(mtx);
}


text:
#include "pthread_impl.h"

int pthread_attr_setdetachstate(pthread_attr_t *a, int state)
{
	if (state > 1U) return EINVAL;
	a->_a_detach = state;
	return 0;
}


text:
#include <threads.h>

int __pthread_key_create(tss_t *, void (*)(void *));

int tss_create(tss_t *tss, tss_dtor_t dtor)
{
	/* Different error returns are possible. C glues them together into
	 * just failure notification. Can't be optimized to a tail call,
	 * unless thrd_error equals EAGAIN. */
	return __pthread_key_create(tss, dtor) ? thrd_error : thrd_success;
}


text:
#include "pthread_impl.h"
#include "syscall.h"

static long sccp(syscall_arg_t nr,
                 syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
                 syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
{
	return (__syscall)(nr, u, v, w, x, y, z);
}

weak_alias(sccp, __syscall_cp_c);

long (__syscall_cp)(syscall_arg_t nr,
                    syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
                    syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
{
	return __syscall_cp_c(nr, u, v, w, x, y, z);
}


text:
#include "pthread_impl.h"

int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a)
{
	*c = (pthread_cond_t){0};
	if (a) {
		c->_c_clock = a->__attr & 0x7fffffff;
		if (a->__attr>>31) c->_c_shared = (void *)-1;
	}
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param)
{
	int r;
	__lock(t->killlock);
	r = t->dead ? ESRCH : -__syscall(SYS_sched_setscheduler, t->tid, policy, param);
	__unlock(t->killlock);
	return r;
}


text:
#include "pthread_impl.h"

int __private_cond_signal(pthread_cond_t *, int);

int pthread_cond_signal(pthread_cond_t *c)
{
	if (!c->_c_shared) return __private_cond_signal(c, 1);
	if (!c->_c_waiters) return 0;
	a_inc(&c->_c_seq);
	__wake(&c->_c_seq, 1, 0);
	return 0;
}


text:
#include "pthread_impl.h"

int __set_thread_area(void *p)
{
#ifdef SYS_set_thread_area
	return __syscall(SYS_set_thread_area, p);
#else
	return -ENOSYS;
#endif
}


text:
#include <threads.h>
#include <errno.h>
#include "syscall.h"

int thrd_sleep(const struct timespec *req, struct timespec *rem)
{
	int ret = __syscall(SYS_nanosleep, req, rem);
	switch (ret) {
	case 0:      return 0;
	case -EINTR: return -1; /* value specified by C11 */
	default:     return -2;
	}
}


text:
#include <threads.h>

int __pthread_once(once_flag *, void (*)(void));

void call_once(once_flag *flag, void (*func)(void))
{
	__pthread_once(flag, func);
}


text:
#include "pthread_impl.h"

int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
{
	int old, own;
	int type = m->_m_type & 15;
	pthread_t self = __pthread_self();
	int tid = self->tid;

	if (!self->robust_list.off) {
		__syscall(SYS_set_robust_list, &self->robust_list, 3*sizeof(long));
		self->robust_list.head = &self->robust_list.head;
		self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
	}

	old = m->_m_lock;
	own = old & 0x7fffffff;
	if (own == tid && (type&3) == PTHREAD_MUTEX_RECURSIVE) {
		if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
		m->_m_count++;
		return 0;
	}
	if (own == 0x40000000) return ENOTRECOVERABLE;

	if (m->_m_type & 128) {
		if (m->_m_waiters) tid |= 0x80000000;
		self->robust_list.pending = &m->_m_next;
	}

	if ((own && (!(own & 0x40000000) || !(type & 4)))
	    || a_cas(&m->_m_lock, old, tid) != old) {
		self->robust_list.pending = 0;
		return EBUSY;
	}

	volatile void *next = self->robust_list.head;
	m->_m_next = next;
	m->_m_prev = &self->robust_list.head;
	if (next != &self->robust_list.head) *(volatile void *volatile *)
		((char *)next - sizeof(void *)) = &m->_m_next;
	self->robust_list.head = &m->_m_next;
	self->robust_list.pending = 0;

	if (own) {
		m->_m_count = 0;
		m->_m_type |= 8;
		return EOWNERDEAD;
	}

	return 0;
}

int __pthread_mutex_trylock(pthread_mutex_t *m)
{
	if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL)
		return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
	return __pthread_mutex_trylock_owner(m);
}

weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock);


text:
#include "pthread_impl.h"

int pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clk)
{
	if (clk < 0 || clk-2U < 2) return EINVAL;
	a->__attr &= 0x80000000;
	a->__attr |= clk;
	return 0;
}


text:
#include <threads.h>
#include "syscall.h"

void thrd_yield()
{
	__syscall(SYS_sched_yield);
}


text:
#include "pthread_impl.h"

int pthread_condattr_init(pthread_condattr_t *a)
{
	*a = (pthread_condattr_t){0};
	return 0;
}


text:
#include <pthread.h>

int pthread_getconcurrency()
{
	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>

int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);

int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{
	int ret = __pthread_create(thr, __ATTRP_C11_THREAD, (void *(*)(void *))func, arg);
	switch (ret) {
	case 0:      return thrd_success;
	case EAGAIN: return thrd_nomem;
	default:     return thrd_error;
	}
}


text:
#include "pthread_impl.h"

int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict m, int *restrict ceiling)
{
	return EINVAL;
}


text:
#include <threads.h>

int __private_cond_signal(cnd_t *, int);

int cnd_signal(cnd_t *c)
{
	/* This internal function never fails, and always returns zero,
	 * which matches the value thrd_success is defined with. */
	return __private_cond_signal(c, 1);
}


text:
#include "pthread_impl.h"

int pthread_rwlock_rdlock(pthread_rwlock_t *rw)
{
	return pthread_rwlock_timedrdlock(rw, 0);
}


text:
#include <stddef.h>
#include "pthread_impl.h"
#include "libc.h"

void *__tls_get_new(size_t *) ATTR_LIBC_VISIBILITY;

void *__tls_get_addr(size_t *v)
{
	pthread_t self = __pthread_self();
#ifdef SHARED
	if (v[0]<=(size_t)self->dtv[0])
		return (char *)self->dtv[v[0]]+v[1];
	return __tls_get_new(v);
#else
	return (char *)self->dtv[1]+v[1];
#endif
}


text:
#include "pthread_impl.h"

int pthread_attr_setguardsize(pthread_attr_t *a, size_t size)
{
	if (size > SIZE_MAX/8) return EINVAL;
	a->_a_guardsize = size - DEFAULT_GUARD_SIZE;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_attr_destroy(pthread_attr_t *a)
{
	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>

int mtx_lock(mtx_t *m)
{
	if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
		return thrd_success;
	/* Calling mtx_timedlock with a null pointer is an extension.
	 * It is convenient, here to avoid duplication of the logic
	 * for return values. */
	return mtx_timedlock(m, 0);
}


text:
#include "pthread_impl.h"

int pthread_mutex_init(pthread_mutex_t *restrict m, const pthread_mutexattr_t *restrict a)
{
	*m = (pthread_mutex_t){0};
	if (a) m->_m_type = a->__attr;
	return 0;
}


text:
#include "pthread_impl.h"

int __private_cond_signal(pthread_cond_t *, int);

int pthread_cond_broadcast(pthread_cond_t *c)
{
	if (!c->_c_shared) return __private_cond_signal(c, -1);
	if (!c->_c_waiters) return 0;
	a_inc(&c->_c_seq);
	__wake(&c->_c_seq, -1, 0);
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_spin_init(pthread_spinlock_t *s, int shared)
{
	return *s = 0;
}


text:
#include "pthread_impl.h"

int pthread_mutex_setprioceiling(pthread_mutex_t *restrict m, int ceiling, int *restrict old)
{
	return EINVAL;
}


text:
#include "pthread_impl.h"
#include <sys/mman.h>

int __munmap(void *, size_t);

static void dummy(void *p)
{
}

int __pthread_join(pthread_t t, void **res)
{
	int tmp;
	pthread_testcancel();
	while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, dummy, 0, 0);
	if (res) *res = t->result;
	if (t->map_base) __munmap(t->map_base, t->map_size);
	return 0;
}

weak_alias(__pthread_join, pthread_join);


text:
#include "pthread_impl.h"

int pthread_spin_trylock(pthread_spinlock_t *s)
{
	return -a_swap(s, 1) & EBUSY;
}


text:
#include "pthread_impl.h"

int pthread_attr_setinheritsched(pthread_attr_t *a, int inherit)
{
	if (inherit > 1U) return EINVAL;
	a->_a_sched = inherit;
	return 0;
}


text:
#include <stdint.h>
#include <threads.h>

int __pthread_join(thrd_t, void**);

int thrd_join(thrd_t t, int *res)
{
        void *pthread_res;
        __pthread_join(t, &pthread_res);
        if (res) *res = (int)(intptr_t)pthread_res;
        return thrd_success;
}


text:
#include "pthread_impl.h"

int __pthread_setcancelstate(int new, int *old)
{
	if (new > 1U) return EINVAL;
	if (!libc.has_thread_pointer) return ENOSYS;
	struct pthread *self = __pthread_self();
	if (old) *old = self->canceldisable;
	self->canceldisable = new;
	return 0;
}

weak_alias(__pthread_setcancelstate, pthread_setcancelstate);


text:
#include "pthread_impl.h"

int pthread_rwlock_unlock(pthread_rwlock_t *rw)
{
	int val, cnt, waiters, new, priv = rw->_rw_shared^128;

	do {
		val = rw->_rw_lock;
		cnt = val & 0x7fffffff;
		waiters = rw->_rw_waiters;
		new = (cnt == 0x7fffffff || cnt == 1) ? 0 : val-1;
	} while (a_cas(&rw->_rw_lock, val, new) != val);

	if (!new && (waiters || val<0))
		__wake(&rw->_rw_lock, cnt, priv);

	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>

int tss_set(tss_t k, void *x)
{
	struct pthread *self = __pthread_self();
	/* Avoid unnecessary COW */
	if (self->tsd[k] != x) {
		self->tsd[k] = x;
		self->tsd_used = 1;
	}
	return thrd_success;
}


text:
#include "pthread_impl.h"

int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
{
	int r, t;

	r = pthread_rwlock_tryrdlock(rw);
	if (r != EBUSY) return r;

	int spins = 100;
	while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();

	while ((r=pthread_rwlock_tryrdlock(rw))==EBUSY) {
		if (!(r=rw->_rw_lock) || (r&0x7fffffff)!=0x7fffffff) continue;
		t = r | 0x80000000;
		a_inc(&rw->_rw_waiters);
		a_cas(&rw->_rw_lock, r, t);
		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, rw->_rw_shared^128);
		a_dec(&rw->_rw_waiters);
		if (r && r != EINTR) return r;
	}
	return r;
}


text:
#include <threads.h>

int cnd_wait(cnd_t *c, mtx_t *m)
{
	/* Calling cnd_timedwait with a null pointer is an extension.
	 * It is convenient here to avoid duplication of the logic
	 * for return values. */
	return cnd_timedwait(c, m, 0);
}


text:
#include "pthread_impl.h"
#include <threads.h>

int __pthread_join(pthread_t, void **);

static int __pthread_detach(pthread_t t)
{
	/* Cannot detach a thread that's already exiting */
	if (a_swap(t->exitlock, 1))
		return __pthread_join(t, 0);
	t->detached = 2;
	__unlock(t->exitlock);
	return 0;
}

weak_alias(__pthread_detach, pthread_detach);
weak_alias(__pthread_detach, thrd_detach);


text:
#include "pthread_impl.h"

int pthread_attr_setscope(pthread_attr_t *a, int scope)
{
	switch (scope) {
	case PTHREAD_SCOPE_SYSTEM:
		return 0;
	case PTHREAD_SCOPE_PROCESS:
		return ENOTSUP;
	default:
		return EINVAL;
	}
}


text:
#include "pthread_impl.h"

int pthread_barrierattr_destroy(pthread_barrierattr_t *a)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_attr_init(pthread_attr_t *a)
{
	*a = (pthread_attr_t){0};
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_rwlock_init(pthread_rwlock_t *restrict rw, const pthread_rwlockattr_t *restrict a)
{
	*rw = (pthread_rwlock_t){0};
	if (a) rw->_rw_shared = a->__attr[0]*128;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_rwlock_trywrlock(pthread_rwlock_t *rw)
{
	if (a_cas(&rw->_rw_lock, 0, 0x7fffffff)) return EBUSY;
	return 0;
}


text:
#define _GNU_SOURCE
#include "pthread_impl.h"
#include "libc.h"
#include <sys/mman.h>

int pthread_getattr_np(pthread_t t, pthread_attr_t *a)
{
	*a = (pthread_attr_t){0};
	a->_a_detach = !!t->detached;
	if (t->stack) {
		a->_a_stackaddr = (uintptr_t)t->stack;
		a->_a_stacksize = t->stack_size - DEFAULT_STACK_SIZE;
	} else {
		char *p = (void *)libc.auxv;
		size_t l = PAGE_SIZE;
		p += -(uintptr_t)p & PAGE_SIZE-1;
		a->_a_stackaddr = (uintptr_t)p;
		while (mremap(p-l-PAGE_SIZE, PAGE_SIZE, 2*PAGE_SIZE, 0)==MAP_FAILED && errno==ENOMEM)
			l += PAGE_SIZE;
		a->_a_stacksize = l - DEFAULT_STACK_SIZE;
	}
	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>

int __pthread_mutex_trylock(mtx_t *);

int mtx_trylock(mtx_t *m)
{
	if (m->_m_type == PTHREAD_MUTEX_NORMAL)
		return (a_cas(&m->_m_lock, 0, EBUSY) & EBUSY) ? thrd_busy : thrd_success;

	int ret = __pthread_mutex_trylock(m);
	switch (ret) {
	default:    return thrd_error;
	case 0:     return thrd_success;
	case EBUSY: return thrd_busy;
	}
}


text:
#include "pthread_impl.h"

void __lock(volatile int *l)
{
	if (libc.threads_minus_1)
		while (a_swap(l, 1)) __wait(l, l+1, 1, 1);
}

void __unlock(volatile int *l)
{
	if (l[0]) {
		a_store(l, 0);
		if (l[1]) __wake(l, 1, 1);
	}
}


text:
#include "pthread_impl.h"

int pthread_kill(pthread_t t, int sig)
{
	int r;
	__lock(t->killlock);
	r = t->dead ? ESRCH : -__syscall(SYS_tkill, t->tid, sig);
	__unlock(t->killlock);
	return r;
}


text:
#define _GNU_SOURCE
#include "pthread_impl.h"
#include "stdio_impl.h"
#include "libc.h"
#include <sys/mman.h>
#include <string.h>

void *__mmap(void *, size_t, int, int, int, off_t);
int __munmap(void *, size_t);
int __mprotect(void *, size_t, int);

static void dummy_0()
{
}
weak_alias(dummy_0, __acquire_ptc);
weak_alias(dummy_0, __release_ptc);
weak_alias(dummy_0, __pthread_tsd_run_dtors);
weak_alias(dummy_0, __do_private_robust_list);
weak_alias(dummy_0, __do_orphaned_stdio_locks);

_Noreturn void __pthread_exit(void *result)
{
	pthread_t self = __pthread_self();
	sigset_t set;

	self->result = result;

	while (self->cancelbuf) {
		void (*f)(void *) = self->cancelbuf->__f;
		void *x = self->cancelbuf->__x;
		self->cancelbuf = self->cancelbuf->__next;
		f(x);
	}

	__pthread_tsd_run_dtors();

	__lock(self->exitlock);

	/* Mark this thread dead before decrementing count */
	__lock(self->killlock);
	self->dead = 1;

	/* Block all signals before decrementing the live thread count.
	 * This is important to ensure that dynamically allocated TLS
	 * is not under-allocated/over-committed, and possibly for other
	 * reasons as well. */
	__block_all_sigs(&set);

	/* Wait to unlock the kill lock, which governs functions like
	 * pthread_kill which target a thread id, until signals have
	 * been blocked. This precludes observation of the thread id
	 * as a live thread (with application code running in it) after
	 * the thread was reported dead by ESRCH being returned. */
	__unlock(self->killlock);

	/* It's impossible to determine whether this is "the last thread"
	 * until performing the atomic decrement, since multiple threads
	 * could exit at the same time. For the last thread, revert the
	 * decrement and unblock signals to give the atexit handlers and
	 * stdio cleanup code a consistent state. */
	if (a_fetch_add(&libc.threads_minus_1, -1)==0) {
		libc.threads_minus_1 = 0;
		__restore_sigs(&set);
		exit(0);
	}

	if (self->locale != &libc.global_locale) {
		a_dec(&libc.uselocale_cnt);
		if (self->locale->ctype_utf8)
			a_dec(&libc.bytelocale_cnt_minus_1);
	}

	__do_private_robust_list();
	__do_orphaned_stdio_locks();

	if (self->detached && self->map_base) {
		/* Detached threads must avoid the kernel clear_child_tid
		 * feature, since the virtual address will have been
		 * unmapped and possibly already reused by a new mapping
		 * at the time the kernel would perform the write. In
		 * the case of threads that started out detached, the
		 * initial clone flags are correct, but if the thread was
		 * detached later (== 2), we need to clear it here. */
		if (self->detached == 2) __syscall(SYS_set_tid_address, 0);

		/* The following call unmaps the thread's stack mapping
		 * and then exits without touching the stack. */
		__unmapself(self->map_base, self->map_size);
	}

	for (;;) __syscall(SYS_exit, 0);
}

void __do_cleanup_push(struct __ptcb *cb)
{
	if (!libc.has_thread_pointer) return;
	struct pthread *self = __pthread_self();
	cb->__next = self->cancelbuf;
	self->cancelbuf = cb;
}

void __do_cleanup_pop(struct __ptcb *cb)
{
	if (!libc.has_thread_pointer) return;
	__pthread_self()->cancelbuf = cb->__next;
}

static int start(void *p)
{
	pthread_t self = p;
	if (self->startlock[0]) {
		__wait(self->startlock, 0, 1, 1);
		if (self->startlock[0]) {
			self->detached = 2;
			pthread_exit(0);
		}
		__restore_sigs(self->sigmask);
	}
	if (self->unblock_cancel)
		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
			SIGPT_SET, 0, _NSIG/8);
	__pthread_exit(self->start(self->start_arg));
	return 0;
}

static int start_c11(void *p)
{
	pthread_t self = p;
	int (*start)(void*) = (int(*)(void*)) self->start;
	__pthread_exit((void *)(uintptr_t)start(self->start_arg));
	return 0;
}

#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE)

/* pthread_key_create.c overrides this */
static volatile size_t dummy = 0;
weak_alias(dummy, __pthread_tsd_size);
static void *dummy_tsd[1] = { 0 };
weak_alias(dummy_tsd, __pthread_tsd_main);

static FILE *volatile dummy_file = 0;
weak_alias(dummy_file, __stdin_used);
weak_alias(dummy_file, __stdout_used);
weak_alias(dummy_file, __stderr_used);

static void init_file_lock(FILE *f)
{
	if (f && f->lock<0) f->lock = 0;
}

void *__copy_tls(unsigned char *);

int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
{
	int ret, c11 = (attrp == __ATTRP_C11_THREAD);
	size_t size, guard;
	struct pthread *self, *new;
	unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
	unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
		| CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS
		| CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED;
	int do_sched = 0;
	pthread_attr_t attr = {0};

	if (!libc.can_do_threads) return ENOSYS;
	self = __pthread_self();
	if (!libc.threaded) {
		for (FILE *f=libc.ofl_head; f; f=f->next)
			init_file_lock(f);
		init_file_lock(__stdin_used);
		init_file_lock(__stdout_used);
		init_file_lock(__stderr_used);
		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
		self->tsd = (void **)__pthread_tsd_main;
		libc.threaded = 1;
	}
	if (attrp && !c11) attr = *attrp;

	__acquire_ptc();

	if (attr._a_stackaddr) {
		size_t need = libc.tls_size + __pthread_tsd_size;
		size = attr._a_stacksize + DEFAULT_STACK_SIZE;
		stack = (void *)(attr._a_stackaddr & -16);
		stack_limit = (void *)(attr._a_stackaddr - size);
		/* Use application-provided stack for TLS only when
		 * it does not take more than ~12% or 2k of the
		 * application's stack space. */
		if (need < size/8 && need < 2048) {
			tsd = stack - __pthread_tsd_size;
			stack = tsd - libc.tls_size;
			memset(stack, 0, need);
		} else {
			size = ROUND(need);
			guard = 0;
		}
	} else {
		guard = ROUND(DEFAULT_GUARD_SIZE + attr._a_guardsize);
		size = guard + ROUND(DEFAULT_STACK_SIZE + attr._a_stacksize
			+ libc.tls_size +  __pthread_tsd_size);
	}

	if (!tsd) {
		if (guard) {
			map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
			if (map == MAP_FAILED) goto fail;
			if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
				__munmap(map, size);
				goto fail;
			}
		} else {
			map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
			if (map == MAP_FAILED) goto fail;
		}
		tsd = map + size - __pthread_tsd_size;
		if (!stack) {
			stack = tsd - libc.tls_size;
			stack_limit = map + guard;
		}
	}

	new = __copy_tls(tsd - libc.tls_size);
	new->map_base = map;
	new->map_size = size;
	new->stack = stack;
	new->stack_size = stack - stack_limit;
	new->start = entry;
	new->start_arg = arg;
	new->self = new;
	new->tsd = (void *)tsd;
	new->locale = &libc.global_locale;
	if (attr._a_detach) {
		new->detached = 1;
		flags -= CLONE_CHILD_CLEARTID;
	}
	if (attr._a_sched) {
		do_sched = new->startlock[0] = 1;
		__block_app_sigs(new->sigmask);
	}
	new->unblock_cancel = self->cancel;
	new->canary = self->canary;

	a_inc(&libc.threads_minus_1);
	ret = __clone((c11 ? start_c11 : start), stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);

	__release_ptc();

	if (do_sched) {
		__restore_sigs(new->sigmask);
	}

	if (ret < 0) {
		a_dec(&libc.threads_minus_1);
		if (map) __munmap(map, size);
		return EAGAIN;
	}

	if (do_sched) {
		ret = __syscall(SYS_sched_setscheduler, new->tid,
			attr._a_policy, &attr._a_prio);
		a_store(new->startlock, ret<0 ? 2 : 0);
		__wake(new->startlock, 1, 1);
		if (ret < 0) return -ret;
	}

	*res = new;
	return 0;
fail:
	__release_ptc();
	return EAGAIN;
}

weak_alias(__pthread_exit, pthread_exit);
weak_alias(__pthread_create, pthread_create);


text:
#include <semaphore.h>
#include <sys/mman.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <pthread.h>
#include "libc.h"

char *__shm_mapname(const char *, char *);

static struct {
	ino_t ino;
	sem_t *sem;
	int refcnt;
} *semtab;
static int lock[2];

#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)

sem_t *sem_open(const char *name, int flags, ...)
{
	va_list ap;
	mode_t mode;
	unsigned value;
	int fd, i, e, slot, first=1, cnt, cs;
	sem_t newsem;
	void *map;
	char tmp[64];
	struct timespec ts;
	struct stat st;
	char buf[NAME_MAX+10];

	if (!(name = __shm_mapname(name, buf)))
		return SEM_FAILED;

	LOCK(lock);
	/* Allocate table if we don't have one yet */
	if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
		UNLOCK(lock);
		return SEM_FAILED;
	}

	/* Reserve a slot in case this semaphore is not mapped yet;
	 * this is necessary because there is no way to handle
	 * failures after creation of the file. */
	slot = -1;
	for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
		cnt += semtab[i].refcnt;
		if (!semtab[i].sem && slot < 0) slot = i;
	}
	/* Avoid possibility of overflow later */
	if (cnt == INT_MAX || slot < 0) {
		errno = EMFILE;
		UNLOCK(lock);
		return SEM_FAILED;
	}
	/* Dummy pointer to make a reservation */
	semtab[slot].sem = (sem_t *)-1;
	UNLOCK(lock);

	flags &= (O_CREAT|O_EXCL);

	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);

	/* Early failure check for exclusive open; otherwise the case
	 * where the semaphore already exists is expensive. */
	if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
		errno = EEXIST;
		goto fail;
	}

	for (;;) {
		/* If exclusive mode is not requested, try opening an
		 * existing file first and fall back to creation. */
		if (flags != (O_CREAT|O_EXCL)) {
			fd = open(name, FLAGS);
			if (fd >= 0) {
				if (fstat(fd, &st) < 0 ||
				    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
					close(fd);
					goto fail;
				}
				close(fd);
				break;
			}
			if (errno != ENOENT)
				goto fail;
		}
		if (!(flags & O_CREAT))
			goto fail;
		if (first) {
			first = 0;
			va_start(ap, flags);
			mode = va_arg(ap, mode_t) & 0666;
			value = va_arg(ap, unsigned);
			va_end(ap);
			if (value > SEM_VALUE_MAX) {
				errno = EINVAL;
				goto fail;
			}
			sem_init(&newsem, 1, value);
		}
		/* Create a temp file with the new semaphore contents
		 * and attempt to atomically link it as the new name */
		clock_gettime(CLOCK_REALTIME, &ts);
		snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
		fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
		if (fd < 0) {
			if (errno == EEXIST) continue;
			goto fail;
		}
		if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
		    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
			close(fd);
			unlink(tmp);
			goto fail;
		}
		close(fd);
		e = link(tmp, name) ? errno : 0;
		unlink(tmp);
		if (!e) break;
		/* Failure is only fatal when doing an exclusive open;
		 * otherwise, next iteration will try to open the
		 * existing file. */
		if (e != EEXIST || flags == (O_CREAT|O_EXCL))
			goto fail;
	}

	/* See if the newly mapped semaphore is already mapped. If
	 * so, unmap the new mapping and use the existing one. Otherwise,
	 * add it to the table of mapped semaphores. */
	LOCK(lock);
	for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
	if (i<SEM_NSEMS_MAX) {
		munmap(map, sizeof(sem_t));
		semtab[slot].sem = 0;
		slot = i;
		map = semtab[i].sem;
	}
	semtab[slot].refcnt++;
	semtab[slot].sem = map;
	semtab[slot].ino = st.st_ino;
	UNLOCK(lock);
	pthread_setcancelstate(cs, 0);
	return map;

fail:
	pthread_setcancelstate(cs, 0);
	LOCK(lock);
	semtab[slot].sem = 0;
	UNLOCK(lock);
	return SEM_FAILED;
}

int sem_close(sem_t *sem)
{
	int i;
	LOCK(lock);
	for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
	if (!--semtab[i].refcnt) {
		semtab[i].sem = 0;
		semtab[i].ino = 0;
	}
	UNLOCK(lock);
	munmap(sem, sizeof *sem);
	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>

static void *__pthread_getspecific(pthread_key_t k)
{
	struct pthread *self = __pthread_self();
	return self->tsd[k];
}

weak_alias(__pthread_getspecific, pthread_getspecific);
weak_alias(__pthread_getspecific, tss_get);


text:
#include <semaphore.h>

int sem_destroy(sem_t *sem)
{
	return 0;
}


text:
#include "pthread_impl.h"

void __vm_lock_impl(int);
void __vm_unlock_impl(void);

int __pthread_mutex_unlock(pthread_mutex_t *m)
{
	pthread_t self;
	int waiters = m->_m_waiters;
	int cont;
	int type = m->_m_type & 15;
	int priv = (m->_m_type & 128) ^ 128;

	if (type != PTHREAD_MUTEX_NORMAL) {
		self = __pthread_self();
		if ((m->_m_lock&0x7fffffff) != self->tid)
			return EPERM;
		if ((type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
			return m->_m_count--, 0;
		if (!priv) {
			self->robust_list.pending = &m->_m_next;
			__vm_lock_impl(+1);
		}
		volatile void *prev = m->_m_prev;
		volatile void *next = m->_m_next;
		*(volatile void *volatile *)prev = next;
		if (next != &self->robust_list.head) *(volatile void *volatile *)
			((char *)next - sizeof(void *)) = prev;
	}
	cont = a_swap(&m->_m_lock, (type & 8) ? 0x40000000 : 0);
	if (type != PTHREAD_MUTEX_NORMAL && !priv) {
		self->robust_list.pending = 0;
		__vm_unlock_impl();
	}
	if (waiters || cont<0)
		__wake(&m->_m_lock, 1, priv);
	return 0;
}

weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock);


text:
#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_spin_lock(pthread_spinlock_t *s)
{
	while (a_swap(s, 1)) a_spin();
	return 0;
}


text:
#include "pthread_impl.h"

void __pthread_testcancel(void);
int __pthread_mutex_lock(pthread_mutex_t *);
int __pthread_mutex_unlock(pthread_mutex_t *);

/*
 * struct waiter
 *
 * Waiter objects have automatic storage on the waiting thread, and
 * are used in building a linked list representing waiters currently
 * waiting on the condition variable or a group of waiters woken
 * together by a broadcast or signal; in the case of signal, this is a
 * degenerate list of one member.
 *
 * Waiter lists attached to the condition variable itself are
 * protected by the lock on the cv. Detached waiter lists are never
 * modified again, but can only be traversed in reverse order, and are
 * protected by the "barrier" locks in each node, which are unlocked
 * in turn to control wake order.
 *
 * Since process-shared cond var semantics do not necessarily allow
 * one thread to see another's automatic storage (they may be in
 * different processes), the waiter list is not used for the
 * process-shared case, but the structure is still used to store data
 * needed by the cancellation cleanup handler.
 */

struct waiter {
	struct waiter *prev, *next;
	int state, barrier, mutex_ret;
	int *notify;
	pthread_mutex_t *mutex;
	pthread_cond_t *cond;
	int shared;
};

/* Self-synchronized-destruction-safe lock functions */

static inline void lock(volatile int *l)
{
	if (a_cas(l, 0, 1)) {
		a_cas(l, 1, 2);
		do __wait(l, 0, 2, 1);
		while (a_cas(l, 0, 2));
	}
}

static inline void unlock(volatile int *l)
{
	if (a_swap(l, 0)==2)
		__wake(l, 1, 1);
}

static inline void unlock_requeue(volatile int *l, volatile int *r, int w)
{
	a_store(l, 0);
	if (w) __wake(l, 1, 1);
	else __syscall(SYS_futex, l, FUTEX_REQUEUE|128, 0, 1, r) != -ENOSYS
		|| __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r);
}

enum {
	WAITING,
	SIGNALED,
	LEAVING,
};

static void unwait(void *arg)
{
	struct waiter *node = arg;

	if (node->shared) {
		pthread_cond_t *c = node->cond;
		pthread_mutex_t *m = node->mutex;
		if (a_fetch_add(&c->_c_waiters, -1) == -0x7fffffff)
			__wake(&c->_c_waiters, 1, 0);
		node->mutex_ret = pthread_mutex_lock(m);
		return;
	}

	int oldstate = a_cas(&node->state, WAITING, LEAVING);

	if (oldstate == WAITING) {
		/* Access to cv object is valid because this waiter was not
		 * yet signaled and a new signal/broadcast cannot return
		 * after seeing a LEAVING waiter without getting notified
		 * via the futex notify below. */

		pthread_cond_t *c = node->cond;
		lock(&c->_c_lock);

		if (c->_c_head == node) c->_c_head = node->next;
		else if (node->prev) node->prev->next = node->next;
		if (c->_c_tail == node) c->_c_tail = node->prev;
		else if (node->next) node->next->prev = node->prev;

		unlock(&c->_c_lock);

		if (node->notify) {
			if (a_fetch_add(node->notify, -1)==1)
				__wake(node->notify, 1, 1);
		}
	} else {
		/* Lock barrier first to control wake order. */
		lock(&node->barrier);
	}

	node->mutex_ret = pthread_mutex_lock(node->mutex);

	if (oldstate == WAITING) return;

	if (!node->next) a_inc(&node->mutex->_m_waiters);

	/* Unlock the barrier that's holding back the next waiter, and
	 * either wake it or requeue it to the mutex. */
	if (node->prev) {
		unlock_requeue(&node->prev->barrier,
			&node->mutex->_m_lock,
			node->mutex->_m_type & 128);
	} else {
		a_dec(&node->mutex->_m_waiters);
	}
}

int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
{
	struct waiter node = { .cond = c, .mutex = m };
	int e, seq, *fut, clock = c->_c_clock;

	if ((m->_m_type&15) && (m->_m_lock&INT_MAX) != __pthread_self()->tid)
		return EPERM;

	if (ts && ts->tv_nsec >= 1000000000UL)
		return EINVAL;

	__pthread_testcancel();

	if (c->_c_shared) {
		node.shared = 1;
		fut = &c->_c_seq;
		seq = c->_c_seq;
		a_inc(&c->_c_waiters);
	} else {
		lock(&c->_c_lock);

		seq = node.barrier = 2;
		fut = &node.barrier;
		node.state = WAITING;
		node.next = c->_c_head;
		c->_c_head = &node;
		if (!c->_c_tail) c->_c_tail = &node;
		else node.next->prev = &node;

		unlock(&c->_c_lock);
	}

	__pthread_mutex_unlock(m);

	do e = __timedwait(fut, seq, clock, ts, unwait, &node, !node.shared);
	while (*fut==seq && (!e || e==EINTR));
	if (e == EINTR) e = 0;

	unwait(&node);

	return node.mutex_ret ? node.mutex_ret : e;
}

int __private_cond_signal(pthread_cond_t *c, int n)
{
	struct waiter *p, *first=0;
	int ref = 0, cur;

	lock(&c->_c_lock);
	for (p=c->_c_tail; n && p; p=p->prev) {
		if (a_cas(&p->state, WAITING, SIGNALED) != WAITING) {
			ref++;
			p->notify = &ref;
		} else {
			n--;
			if (!first) first=p;
		}
	}
	/* Split the list, leaving any remainder on the cv. */
	if (p) {
		if (p->next) p->next->prev = 0;
		p->next = 0;
	} else {
		c->_c_head = 0;
	}
	c->_c_tail = p;
	unlock(&c->_c_lock);

	/* Wait for any waiters in the LEAVING state to remove
	 * themselves from the list before returning or allowing
	 * signaled threads to proceed. */
	while ((cur = ref)) __wait(&ref, 0, cur, 1);

	/* Allow first signaled waiter, if any, to proceed. */
	if (first) unlock(&first->barrier);

	return 0;
}

weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait);


text:
#include "pthread_impl.h"

int pthread_rwlock_destroy(pthread_rwlock_t *rw)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_attr_setschedparam(pthread_attr_t *restrict a, const struct sched_param *restrict param)
{
	a->_a_prio = param->sched_priority;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
{
	*a = (pthread_rwlockattr_t){0};
	return 0;
}