text:
#include "stdio_impl.h"
#include <wchar.h>

wint_t putwchar(wchar_t c)
{
	return fputwc(c, stdout);
}

weak_alias(putwchar, putwchar_unlocked);


text:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "stdio_impl.h"

void perror(const char *msg)
{
	FILE *f = stderr;
	char *errstr = strerror(errno);

	FLOCK(f);

	if (msg && *msg) {
		fwrite(msg, strlen(msg), 1, f);
		fputc(':', f);
		fputc(' ', f);
	}
	fwrite(errstr, strlen(errstr), 1, f);
	fputc('\n', f);

	FUNLOCK(f);
}

repo_language: C

mime_type: text/x-c


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

int getw(FILE *f)
{
	int x;
	return fread(&x, sizeof x, 1, f) ? x : EOF;
}


text:
#include "stdio_impl.h"

int getchar_unlocked(void)
{
	return getc_unlocked(stdin);
}


text:
#include "stdio_impl.h"
#include <errno.h>
#include <limits.h>
#include <string.h>

struct cookie {
	char **bufp;
	size_t *sizep;
	size_t pos;
	char *buf;
	size_t len;
	size_t space;
};

static off_t ms_seek(FILE *f, off_t off, int whence)
{
	ssize_t base;
	struct cookie *c = f->cookie;
	if (whence>2U) {
fail:
		errno = EINVAL;
		return -1;
	}
	base = (size_t [3]){0, c->pos, c->len}[whence];
	if (off < -base || off > SSIZE_MAX-base) goto fail;
	return c->pos = base+off;
}

static size_t ms_write(FILE *f, const unsigned char *buf, size_t len)
{
	struct cookie *c = f->cookie;
	size_t len2 = f->wpos - f->wbase;
	char *newbuf;
	if (len2) {
		f->wpos = f->wbase;
		if (ms_write(f, f->wbase, len2) < len2) return 0;
	}
	if (len + c->pos >= c->space) {
		len2 = 2*c->space+1 | c->pos+len+1;
		newbuf = realloc(c->buf, len2);
		if (!newbuf) return 0;
		*c->bufp = c->buf = newbuf;
		memset(c->buf + c->space, 0, len2 - c->space);
		c->space = len2;
	}
	memcpy(c->buf+c->pos, buf, len);
	c->pos += len;
	if (c->pos >= c->len) c->len = c->pos;
	*c->sizep = c->pos;
	return len;
}

static int ms_close(FILE *f)
{
	return 0;
}

FILE *open_memstream(char **bufp, size_t *sizep)
{
	FILE *f;
	struct cookie *c;
	if (!(f=malloc(sizeof *f + sizeof *c + BUFSIZ))) return 0;
	memset(f, 0, sizeof *f + sizeof *c);
	f->cookie = c = (void *)(f+1);

	c->bufp = bufp;
	c->sizep = sizep;
	c->pos = c->len = c->space = 0;
	c->buf = 0;

	f->flags = F_NORD;
	f->fd = -1;
	f->buf = (void *)(c+1);
	f->buf_size = BUFSIZ;
	f->lbf = EOF;
	f->write = ms_write;
	f->seek = ms_seek;
	f->close = ms_close;

	if (!libc.threaded) f->lock = -1;

	OFLLOCK();
	f->next = libc.ofl_head;
	if (libc.ofl_head) libc.ofl_head->prev = f;
	libc.ofl_head = f;
	OFLUNLOCK();

	return f;
}


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

int remove(const char *path)
{
#ifdef SYS_unlink
	int r = __syscall(SYS_unlink, path);
#else
	int r = __syscall(SYS_unlinkat, AT_FDCWD, path, 0);
#endif
#ifdef SYS_rmdir
	if (r==-EISDIR) r = __syscall(SYS_rmdir, path);
#else
	if (r==-EISDIR) r = __syscall(SYS_unlinkat, AT_FDCWD, path, AT_REMOVEDIR);
#endif
	return __syscall_ret(r);
}


text:
#include "stdio_impl.h"

static unsigned char buf[BUFSIZ+UNGET];
static FILE f = {
	.buf = buf+UNGET,
	.buf_size = sizeof buf-UNGET,
	.fd = 1,
	.flags = F_PERM | F_NORD,
	.lbf = '\n',
	.write = __stdout_write,
	.seek = __stdio_seek,
	.close = __stdio_close,
	.lock = -1,
};
FILE *const stdout = &f;
FILE *volatile __stdout_used = &f;


text:
#include "stdio_impl.h"
#include <errno.h>
#include <string.h>
#include <inttypes.h>

struct cookie {
	size_t pos, len, size;
	unsigned char *buf;
	int mode;
};

static off_t mseek(FILE *f, off_t off, int whence)
{
	ssize_t base;
	struct cookie *c = f->cookie;
	if (whence>2U) {
fail:
		errno = EINVAL;
		return -1;
	}
	base = (size_t [3]){0, c->pos, c->len}[whence];
	if (off < -base || off > (ssize_t)c->size-base) goto fail;
	return c->pos = base+off;
}

static size_t mread(FILE *f, unsigned char *buf, size_t len)
{
	struct cookie *c = f->cookie;
	size_t rem = c->len - c->pos;
	if (c->pos > c->len) rem = 0;
	if (len > rem) {
		len = rem;
		f->flags |= F_EOF;
	}
	memcpy(buf, c->buf+c->pos, len);
	c->pos += len;
	rem -= len;
	if (rem > f->buf_size) rem = f->buf_size;
	f->rpos = f->buf;
	f->rend = f->buf + rem;
	memcpy(f->rpos, c->buf+c->pos, rem);
	c->pos += rem;
	return len;
}

static size_t mwrite(FILE *f, const unsigned char *buf, size_t len)
{
	struct cookie *c = f->cookie;
	size_t rem;
	size_t len2 = f->wpos - f->wbase;
	if (len2) {
		f->wpos = f->wbase;
		if (mwrite(f, f->wpos, len2) < len2) return 0;
	}
	if (c->mode == 'a') c->pos = c->len;
	rem = c->size - c->pos;
	if (len > rem) len = rem;
	memcpy(c->buf+c->pos, buf, len);
	c->pos += len;
	if (c->pos > c->len) {
		c->len = c->pos;
		if (c->len < c->size) c->buf[c->len] = 0;
		else if ((f->flags&F_NORD) && c->size) c->buf[c->size-1] = 0;
	}
	return len;
}

static int mclose(FILE *m)
{
	return 0;
}

FILE *fmemopen(void *restrict buf, size_t size, const char *restrict mode)
{
	FILE *f;
	struct cookie *c;
	int plus = !!strchr(mode, '+');

	if (!size || !strchr("rwa", *mode)) {
		errno = EINVAL;
		return 0;
	}

	if (!buf && size > SIZE_MAX-sizeof(FILE)-BUFSIZ-UNGET) {
		errno = ENOMEM;
		return 0;
	}

	f = calloc(sizeof *f + sizeof *c + UNGET + BUFSIZ + (buf?0:size), 1);
	if (!f) return 0;
	f->cookie = c = (void *)(f+1);
	f->fd = -1;
	f->lbf = EOF;
	f->buf = (unsigned char *)(c+1) + UNGET;
	f->buf_size = BUFSIZ;
	if (!buf) buf = f->buf + BUFSIZ;

	c->buf = buf;
	c->size = size;
	c->mode = *mode;

	if (!plus) f->flags = (*mode == 'r') ? F_NOWR : F_NORD;
	if (*mode == 'r') c->len = size;
	else if (*mode == 'a') c->len = c->pos = strnlen(buf, size);

	f->read = mread;
	f->write = mwrite;
	f->seek = mseek;
	f->close = mclose;

	if (!libc.threaded) f->lock = -1;

	OFLLOCK();
	f->next = libc.ofl_head;
	if (libc.ofl_head) libc.ofl_head->prev = f;
	libc.ofl_head = f;
	OFLUNLOCK();

	return f;
}


text:
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <spawn.h>
#include "stdio_impl.h"
#include "syscall.h"

extern char **__environ;

FILE *popen(const char *cmd, const char *mode)
{
	int p[2], op, e;
	pid_t pid;
	FILE *f;
	posix_spawn_file_actions_t fa;

	if (*mode == 'r') {
		op = 0;
	} else if (*mode == 'w') {
		op = 1;
	} else {
		errno = EINVAL;
		return 0;
	}

	if (pipe2(p, O_CLOEXEC)) return NULL;
	f = fdopen(p[op], mode);
	if (!f) {
		__syscall(SYS_close, p[0]);
		__syscall(SYS_close, p[1]);
		return NULL;
	}
	FLOCK(f);

	/* If the child's end of the pipe happens to already be on the final
	 * fd number to which it will be assigned (either 0 or 1), it must
	 * be moved to a different fd. Otherwise, there is no safe way to
	 * remove the close-on-exec flag in the child without also creating
	 * a file descriptor leak race condition in the parent. */
	if (p[1-op] == 1-op) {
		int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0);
		if (tmp < 0) {
			e = errno;
			goto fail;
		}
		__syscall(SYS_close, p[1-op]);
		p[1-op] = tmp;
	}

	e = ENOMEM;
	if (!posix_spawn_file_actions_init(&fa)) {
		if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {
			if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,
			    (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {
				posix_spawn_file_actions_destroy(&fa);
				f->pipe_pid = pid;
				if (!strchr(mode, 'e'))
					fcntl(p[op], F_SETFD, 0);
				__syscall(SYS_close, p[1-op]);
				FUNLOCK(f);
				return f;
			}
		}
		posix_spawn_file_actions_destroy(&fa);
	}
fail:
	fclose(f);
	__syscall(SYS_close, p[1-op]);

	errno = e;
	return 0;
}


text:
#include "stdio_impl.h"

static size_t wrap_write(FILE *f, const unsigned char *buf, size_t len)
{
	return __stdio_write(f, buf, len);
}

int vdprintf(int fd, const char *restrict fmt, va_list ap)
{
	FILE f = {
		.fd = fd, .lbf = EOF, .write = wrap_write,
		.buf = (void *)fmt, .buf_size = 0,
		.lock = -1
	};
	return vfprintf(&f, fmt, ap);
}


text:
#include <stdio.h>
#include <fcntl.h>
#include "syscall.h"

int rename(const char *old, const char *new)
{
#ifdef SYS_rename
	return syscall(SYS_rename, old, new);
#else
	return syscall(SYS_renameat, AT_FDCWD, old, AT_FDCWD, new);
#endif
}


text:
#include "stdio_impl.h"
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <wchar.h>

struct cookie {
	wchar_t *ws;
	size_t l;
};

static size_t sw_write(FILE *f, const unsigned char *s, size_t l)
{
	size_t l0 = l;
	int i = 0;
	struct cookie *c = f->cookie;
	if (s!=f->wbase && sw_write(f, f->wbase, f->wpos-f->wbase)==-1)
		return -1;
	while (c->l && l && (i=mbtowc(c->ws, (void *)s, l))>=0) {
		s+=i;
		l-=i;
		c->l--;
		c->ws++;
	}
	*c->ws = 0;
	return i<0 ? i : l0;
}

int vswprintf(wchar_t *restrict s, size_t n, const wchar_t *restrict fmt, va_list ap)
{
	int r;
	FILE f;
	unsigned char buf[256];
	struct cookie c = { s, n-1 };

	memset(&f, 0, sizeof(FILE));
	f.lbf = EOF;
	f.write = sw_write;
	f.buf_size = sizeof buf;
	f.buf = buf;
	f.lock = -1;
	f.cookie = &c;
	if (!n) {
		return -1;
	} else if (n > INT_MAX) {
		errno = EOVERFLOW;
		return -1;
	}
	r = vfwprintf(&f, fmt, ap);
	sw_write(&f, 0, 0);
	return r>=n ? -1 : r;
}


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

int vsprintf(char *restrict s, const char *restrict fmt, va_list ap)
{
	return vsnprintf(s, INT_MAX, fmt, ap);
}


text:
#include "stdio_impl.h"

/* This function makes no attempt to protect the user from his/her own
 * stupidity. If called any time but when then ISO C standard specifically
 * allows it, all hell can and will break loose, especially with threads!
 *
 * This implementation ignores all arguments except the buffering type,
 * and uses the existing buffer allocated alongside the FILE object.
 * In the case of stderr where the preexisting buffer is length 1, it
 * is not possible to set line buffering or full buffering. */

int setvbuf(FILE *restrict f, char *restrict buf, int type, size_t size)
{
	f->lbf = EOF;

	if (type == _IONBF)
		f->buf_size = 0;
	else if (type == _IOLBF)
		f->lbf = '\n';

	f->flags |= F_SVB;

	return 0;
}


text:
#include "stdio_impl.h"
#include <limits.h>
#include <errno.h>

off_t __ftello_unlocked(FILE *f)
{
	off_t pos = f->seek(f, 0,
		(f->flags & F_APP) && f->wpos > f->wbase
		? SEEK_END : SEEK_CUR);
	if (pos < 0) return pos;

	/* Adjust for data in buffer. */
	return pos - (f->rend - f->rpos) + (f->wpos - f->wbase);
}

off_t __ftello(FILE *f)
{
	off_t pos;
	FLOCK(f);
	pos = __ftello_unlocked(f);
	FUNLOCK(f);
	return pos;
}

long ftell(FILE *f)
{
	off_t pos = __ftello(f);
	if (pos > LONG_MAX) {
		errno = EOVERFLOW;
		return -1;
	}
	return pos;
}

weak_alias(__ftello, ftello);

LFS64(ftello);


text:
#include "stdio_impl.h"

int fsetpos(FILE *f, const fpos_t *pos)
{
	return __fseeko(f, *(const off_t *)pos, SEEK_SET);
}

LFS64(fsetpos);


text:
#include "stdio_impl.h"
#include <sys/uio.h>
#include <pthread.h>

static void cleanup(void *p)
{
	FILE *f = p;
	if (!f->lockcount) __unlockfile(f);
}

size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
{
	struct iovec iovs[2] = {
		{ .iov_base = f->wbase, .iov_len = f->wpos-f->wbase },
		{ .iov_base = (void *)buf, .iov_len = len }
	};
	struct iovec *iov = iovs;
	size_t rem = iov[0].iov_len + iov[1].iov_len;
	int iovcnt = 2;
	ssize_t cnt;
	for (;;) {
		pthread_cleanup_push(cleanup, f);
		cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
		pthread_cleanup_pop(0);
		if (cnt == rem) {
			f->wend = f->buf + f->buf_size;
			f->wpos = f->wbase = f->buf;
			return len;
		}
		if (cnt < 0) {
			f->wpos = f->wbase = f->wend = 0;
			f->flags |= F_ERR;
			return iovcnt == 2 ? 0 : len-iov[0].iov_len;
		}
		rem -= cnt;
		if (cnt > iov[0].iov_len) {
			f->wpos = f->wbase = f->buf;
			cnt -= iov[0].iov_len;
			iov++; iovcnt--;
		} else if (iovcnt == 2) {
			f->wbase += cnt;
		}
		iov[0].iov_base = (char *)iov[0].iov_base + cnt;
		iov[0].iov_len -= cnt;
	}
}


text:
#include "stdio_impl.h"

int fgetpos(FILE *restrict f, fpos_t *restrict pos)
{
	off_t off = __ftello(f);
	if (off < 0) return -1;
	*(off_t *)pos = off;
	return 0;
}

LFS64(fgetpos);


text:
#include <stdio.h>
#include <stdarg.h>
#include "libc.h"

int fscanf(FILE *restrict f, const char *restrict fmt, ...)
{
	int ret;
	va_list ap;
	va_start(ap, fmt);
	ret = vfscanf(f, fmt, ap);
	va_end(ap);
	return ret;
}

weak_alias(fscanf, __isoc99_fscanf);


text:
#include "stdio_impl.h"
#include <string.h>

size_t __string_read(FILE *f, unsigned char *buf, size_t len)
{
	char *src = f->cookie;
	size_t k = len+256;
	char *end = memchr(src, 0, k);
	if (end) k = end-src;
	if (k < len) len = k;
	memcpy(buf, src, len);
	f->rpos = (void *)(src+len);
	f->rend = (void *)(src+k);
	f->cookie = src+k;
	return len;
}


text:
#include "stdio_impl.h"

int (putc_unlocked)(int c, FILE *f)
{
	return putc_unlocked(c, f);
}

weak_alias(putc_unlocked, fputc_unlocked);
weak_alias(putc_unlocked, _IO_putc_unlocked);


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

void setlinebuf(FILE *f)
{
	setvbuf(f, 0, _IOLBF, 0);
}


text:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <wchar.h>
#include <wctype.h>
#include <limits.h>
#include <string.h>

#include "stdio_impl.h"
#include "shgetc.h"
#include "intscan.h"
#include "floatscan.h"
#include "libc.h"

#define SIZE_hh -2
#define SIZE_h  -1
#define SIZE_def 0
#define SIZE_l   1
#define SIZE_L   2
#define SIZE_ll  3

static void store_int(void *dest, int size, unsigned long long i)
{
	if (!dest) return;
	switch (size) {
	case SIZE_hh:
		*(char *)dest = i;
		break;
	case SIZE_h:
		*(short *)dest = i;
		break;
	case SIZE_def:
		*(int *)dest = i;
		break;
	case SIZE_l:
		*(long *)dest = i;
		break;
	case SIZE_ll:
		*(long long *)dest = i;
		break;
	}
}

static void *arg_n(va_list ap, unsigned int n)
{
	void *p;
	unsigned int i;
	va_list ap2;
	va_copy(ap2, ap);
	for (i=n; i>1; i--) va_arg(ap2, void *);
	p = va_arg(ap2, void *);
	va_end(ap2);
	return p;
}

static int in_set(const wchar_t *set, int c)
{
	int j;
	const wchar_t *p = set;
	if (*p == '-') {
		if (c=='-') return 1;
		p++;
	} else if (*p == ']') {
		if (c==']') return 1;
		p++;
	}
	for (; *p && *p != ']'; p++) {
		if (*p=='-' && p[1] && p[1] != ']')
			for (j=p++[-1]; j<*p; j++)
				if (c==j) return 1;
		if (c==*p) return 1;
	}
	return 0;
}

#if 1
#undef getwc
#define getwc(f) \
	((f)->rpos < (f)->rend && *(f)->rpos < 128 ? *(f)->rpos++ : (getwc)(f))

#undef ungetwc
#define ungetwc(c,f) \
	((f)->rend && (c)<128U ? *--(f)->rpos : ungetwc((c),(f)))
#endif

int vfwscanf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
{
	int width;
	int size;
	int alloc;
	const wchar_t *p;
	int c, t;
	char *s;
	wchar_t *wcs;
	void *dest=NULL;
	int invert;
	int matches=0;
	off_t pos = 0, cnt;
	static const char size_pfx[][3] = { "hh", "h", "", "l", "L", "ll" };
	char tmp[3*sizeof(int)+10];
	const wchar_t *set;
	size_t i, k;

	FLOCK(f);

	f->mode |= f->mode+1;

	for (p=fmt; *p; p++) {

		alloc = 0;

		if (iswspace(*p)) {
			while (iswspace(p[1])) p++;
			while (iswspace((c=getwc(f)))) pos++;
			ungetwc(c, f);
			continue;
		}
		if (*p != '%' || p[1] == '%') {
			p += *p=='%';
			c = getwc(f);
			if (c!=*p) {
				ungetwc(c, f);
				if (c<0) goto input_fail;
				goto match_fail;
			}
			pos++;
			continue;
		}

		p++;
		if (*p=='*') {
			dest = 0; p++;
		} else if (iswdigit(*p) && p[1]=='$') {
			dest = arg_n(ap, *p-'0'); p+=2;
		} else {
			dest = va_arg(ap, void *);
		}

		for (width=0; iswdigit(*p); p++) {
			width = 10*width + *p - '0';
		}

		if (*p=='m') {
			wcs = 0;
			s = 0;
			alloc = !!dest;
			p++;
		} else {
			alloc = 0;
		}

		size = SIZE_def;
		switch (*p++) {
		case 'h':
			if (*p == 'h') p++, size = SIZE_hh;
			else size = SIZE_h;
			break;
		case 'l':
			if (*p == 'l') p++, size = SIZE_ll;
			else size = SIZE_l;
			break;
		case 'j':
			size = SIZE_ll;
			break;
		case 'z':
		case 't':
			size = SIZE_l;
			break;
		case 'L':
			size = SIZE_L;
			break;
		case 'd': case 'i': case 'o': case 'u': case 'x':
		case 'a': case 'e': case 'f': case 'g':
		case 'A': case 'E': case 'F': case 'G': case 'X':
		case 's': case 'c': case '[':
		case 'S': case 'C':
		case 'p': case 'n':
			p--;
			break;
		default:
			goto fmt_fail;
		}

		t = *p;

		/* Transform S,C -> ls,lc */
		if ((t&0x2f)==3) {
			size = SIZE_l;
			t |= 32;
		}

		if (t != 'n') {
			if (t != '[' && (t|32) != 'c')
				while (iswspace((c=getwc(f)))) pos++;
			else
				c=getwc(f);
			if (c < 0) goto input_fail;
			ungetwc(c, f);
		}

		switch (t) {
		case 'n':
			store_int(dest, size, pos);
			/* do not increment match count, etc! */
			continue;

		case 's':
		case 'c':
		case '[':
			if (t == 'c') {
				if (width<1) width = 1;
				invert = 1;
				set = L"";
			} else if (t == 's') {
				invert = 1;
				set = (const wchar_t[]){
					' ', '\t', '\n', '\r', 11, 12,  0x0085,
					0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
					0x2006, 0x2008, 0x2009, 0x200a,
					0x2028, 0x2029, 0x205f, 0x3000, 0 };
			} else {
				if (*++p == '^') p++, invert = 1;
				else invert = 0;
				set = p;
				if (*p==']') p++;
				while (*p!=']') {
					if (!*p) goto fmt_fail;
					p++;
				}
			}

			s = (size == SIZE_def) ? dest : 0;
			wcs = (size == SIZE_l) ? dest : 0;

			int gotmatch = 0;

			if (width < 1) width = -1;

			i = 0;
			if (alloc) {
				k = t=='c' ? width+1U : 31;
				if (size == SIZE_l) {
					wcs = malloc(k*sizeof(wchar_t));
					if (!wcs) goto alloc_fail;
				} else {
					s = malloc(k);
					if (!s) goto alloc_fail;
				}
			}
			while (width) {
				if ((c=getwc(f))<0) break;
				if (in_set(set, c) == invert)
					break;
				if (wcs) {
					wcs[i++] = c;
					if (alloc && i==k) {
						k += k+1;
						wchar_t *tmp = realloc(wcs, k*sizeof(wchar_t));
						if (!tmp) goto alloc_fail;
						wcs = tmp;
					}
				} else if (size != SIZE_l) {
					int l = wctomb(s?s+i:tmp, c);
					if (l<0) goto input_fail;
					i += l;
					if (alloc && i > k-4) {
						k += k+1;
						char *tmp = realloc(s, k);
						if (!tmp) goto alloc_fail;
						s = tmp;
					}
				}
				pos++;
				width-=(width>0);
				gotmatch=1;
			}
			if (width) {
				ungetwc(c, f);
				if (t == 'c' || !gotmatch) goto match_fail;
			}

			if (alloc) {
				if (size == SIZE_l) *(wchar_t **)dest = wcs;
				else *(char **)dest = s;
			}
			if (t != 'c') {
				if (wcs) wcs[i] = 0;
				if (s) s[i] = 0;
			}
			break;

		case 'd': case 'i': case 'o': case 'u': case 'x':
		case 'a': case 'e': case 'f': case 'g':
		case 'A': case 'E': case 'F': case 'G': case 'X':
		case 'p':
			if (width < 1) width = 0;
			snprintf(tmp, sizeof tmp, "%.*s%.0d%s%c%%lln",
				1+!dest, "%*", width, size_pfx[size+2], t);
			cnt = 0;
			if (fscanf(f, tmp, dest?dest:&cnt, &cnt) == -1)
				goto input_fail;
			else if (!cnt)
				goto match_fail;
			pos += cnt;
			break;
		default:
			goto fmt_fail;
		}

		if (dest) matches++;
	}
	if (0) {
fmt_fail:
alloc_fail:
input_fail:
		if (!matches) matches--;
match_fail:
		if (alloc) {
			free(s);
			free(wcs);
		}
	}
	FUNLOCK(f);
	return matches;
}

weak_alias(vfwscanf,__isoc99_vfwscanf);


text:
#include <stdio.h>

int getchar(void)
{
	return fgetc(stdin);
}


text:
#include <stdio.h>

void setbuf(FILE *restrict f, char *restrict buf)
{
	setvbuf(f, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
}


text:
#include "stdio_impl.h"

int getc(FILE *f)
{
	int c;
	if (f->lock < 0 || !__lockfile(f))
		return getc_unlocked(f);
	c = getc_unlocked(f);
	__unlockfile(f);
	return c;
}

weak_alias(getc, _IO_getc);


text:
#include "stdio_impl.h"
#include <string.h>

#define MIN(a,b) ((a)<(b) ? (a) : (b))

char *fgets(char *restrict s, int n, FILE *restrict f)
{
	char *p = s;
	unsigned char *z;
	size_t k;
	int c;

	FLOCK(f);

	if (n--<=1) {
		f->mode |= f->mode-1;
		FUNLOCK(f);
		if (n) return 0;
		*s = 0;
		return s;
	}

	while (n) {
		z = memchr(f->rpos, '\n', f->rend - f->rpos);
		k = z ? z - f->rpos + 1 : f->rend - f->rpos;
		k = MIN(k, n);
		memcpy(p, f->rpos, k);
		f->rpos += k;
		p += k;
		n -= k;
		if (z || !n) break;
		if ((c = getc_unlocked(f)) < 0) {
			if (p==s || !feof(f)) s = 0;
			break;
		}
		n--;
		if ((*p++ = c) == '\n') break;
	}
	if (s) *p = 0;

	FUNLOCK(f);

	return s;
}

weak_alias(fgets, fgets_unlocked);


text:
#include "stdio_impl.h"

#undef feof

int feof(FILE *f)
{
	FLOCK(f);
	int ret = !!(f->flags & F_EOF);
	FUNLOCK(f);
	return ret;
}

weak_alias(feof, feof_unlocked);
weak_alias(feof, _IO_feof_unlocked);


text:
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include "syscall.h"
#include "libc.h"

void __procfdname(char *, unsigned);

int fstat(int fd, struct stat *st)
{
	int ret = __syscall(SYS_fstat, fd, st);
	if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
		return __syscall_ret(ret);

	char buf[15+3*sizeof(int)];
	__procfdname(buf, fd);
#ifdef SYS_stat
	return syscall(SYS_stat, buf, st);
#else
	return syscall(SYS_fstatat, AT_FDCWD, buf, st, 0);
#endif
}

LFS64(fstat);


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

void __procfdname(char *, unsigned);

int fchmod(int fd, mode_t mode)
{
	int ret = __syscall(SYS_fchmod, fd, mode);
	if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
		return __syscall_ret(ret);

	char buf[15+3*sizeof(int)];
	__procfdname(buf, fd);
#ifdef SYS_chmod
	return syscall(SYS_chmod, buf, mode);
#else
	return syscall(SYS_fchmodat, AT_FDCWD, buf, mode);
#endif
}


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

int mknod(const char *path, mode_t mode, dev_t dev)
{
#ifdef SYS_mknod
	return syscall(SYS_mknod, path, mode, dev);
#else
	return syscall(SYS_mknodat, AT_FDCWD, path, mode, dev);
#endif
}


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

int __statfs(const char *path, struct statfs *buf)
{
	*buf = (struct statfs){0};
#ifdef SYS_statfs64
	return syscall(SYS_statfs64, path, sizeof *buf, buf);
#else
	return syscall(SYS_statfs, path, buf);
#endif
}

int __fstatfs(int fd, struct statfs *buf)
{
	*buf = (struct statfs){0};
#ifdef SYS_fstatfs64
	return syscall(SYS_fstatfs64, fd, sizeof *buf, buf);
#else
	return syscall(SYS_fstatfs, fd, buf);
#endif
}

weak_alias(__statfs, statfs);
weak_alias(__fstatfs, fstatfs);

static void fixup(struct statvfs *out, const struct statfs *in)
{
	*out = (struct statvfs){0};
	out->f_bsize = in->f_bsize;
	out->f_frsize = in->f_frsize ? in->f_frsize : in->f_bsize;
	out->f_blocks = in->f_blocks;
	out->f_bfree = in->f_bfree;
	out->f_bavail = in->f_bavail;
	out->f_files = in->f_files;
	out->f_ffree = in->f_ffree;
	out->f_favail = in->f_ffree;
	out->f_fsid = in->f_fsid.__val[0];
	out->f_flag = in->f_flags;
	out->f_namemax = in->f_namelen;
}

int statvfs(const char *restrict path, struct statvfs *restrict buf)
{
	struct statfs kbuf;
	if (__statfs(path, &kbuf)<0) return -1;
	fixup(buf, &kbuf);
	return 0;
}

int fstatvfs(int fd, struct statvfs *buf)
{
	struct statfs kbuf;
	if (__fstatfs(fd, &kbuf)<0) return -1;
	fixup(buf, &kbuf);
	return 0;
}

LFS64(statvfs);
LFS64(statfs);
LFS64(fstatvfs);
LFS64(fstatfs);


text:
#include <sys/stat.h>

int futimens(int fd, const struct timespec times[2])
{
	return utimensat(fd, 0, times, 0);
}


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

int lchmod(const char *path, mode_t mode)
{
	return fchmodat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW);
}


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

int fstatat(int fd, const char *restrict path, struct stat *restrict buf, int flag)
{
	return syscall(SYS_fstatat, fd, path, buf, flag);
}

LFS64(fstatat);


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

int mkdirat(int fd, const char *path, mode_t mode)
{
	return syscall(SYS_mkdirat, fd, path, mode);
}


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

int __futimesat(int dirfd, const char *pathname, const struct timeval times[2])
{
	struct timespec ts[2];
	if (times) {
		int i;
		for (i=0; i<2; i++) {
			if (times[i].tv_usec >= 1000000ULL)
				return __syscall_ret(-EINVAL);
			ts[i].tv_sec = times[i].tv_sec;
			ts[i].tv_nsec = times[i].tv_usec * 1000;
		}
	}
	return utimensat(dirfd, pathname, times ? ts : 0, 0);
}

weak_alias(__futimesat, futimesat);


text:
#include <sys/stat.h>

int mkfifoat(int fd, const char *path, mode_t mode)
{
	return mknodat(fd, path, mode | S_IFIFO, 0);
}


text:
#include <sys/stat.h>

int mkfifo(const char *path, mode_t mode)
{
	return mknod(path, mode | S_IFIFO, 0);
}


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

mode_t umask(mode_t mode)
{
	return syscall(SYS_umask, mode);
}


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

int stat(const char *restrict path, struct stat *restrict buf)
{
#ifdef SYS_stat
	return syscall(SYS_stat, path, buf);
#else
	return syscall(SYS_fstatat, AT_FDCWD, path, buf, 0);
#endif
}

LFS64(stat);


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

int utimensat(int fd, const char *path, const struct timespec times[2], int flags)
{
	int r = __syscall(SYS_utimensat, fd, path, times, flags);
#ifdef SYS_futimesat
	if (r != -ENOSYS || flags) return __syscall_ret(r);
	struct timeval *tv = 0, tmp[2];
	if (times) {
		int i;
		tv = tmp;
		for (i=0; i<2; i++) {
			if (times[i].tv_nsec >= 1000000000ULL) {
				if (times[i].tv_nsec == UTIME_NOW &&
				    times[1-i].tv_nsec == UTIME_NOW) {
					tv = 0;
					break;
				}
				if (times[i].tv_nsec == UTIME_OMIT)
					return __syscall_ret(-ENOSYS);
				return __syscall_ret(-EINVAL);
			}
			tmp[i].tv_sec = times[i].tv_sec;
			tmp[i].tv_usec = times[i].tv_nsec / 1000;
		}
	}

	r = __syscall(SYS_futimesat, fd, path, tv);
	if (r != -ENOSYS || fd != AT_FDCWD) return __syscall_ret(r);
	r = __syscall(SYS_utimes, path, tv);
#endif
	return __syscall_ret(r);
}


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

int lstat(const char *restrict path, struct stat *restrict buf)
{
#ifdef SYS_lstat
	return syscall(SYS_lstat, path, buf);
#else
	return syscall(SYS_fstatat, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
#endif
}

LFS64(lstat);


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

int mknodat(int fd, const char *path, mode_t mode, dev_t dev)
{
	return syscall(SYS_mknodat, fd, path, mode, dev);
}


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

int chmod(const char *path, mode_t mode)
{
#ifdef SYS_chmod
	return syscall(SYS_chmod, path, mode);
#else
	return syscall(SYS_fchmodat, AT_FDCWD, path, mode);
#endif
}


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

int mkdir(const char *path, mode_t mode)
{
#ifdef SYS_mkdir
	return syscall(SYS_mkdir, path, mode);
#else
	return syscall(SYS_mkdirat, AT_FDCWD, path, mode);
#endif
}


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

void __procfdname(char *, unsigned);

int fchmodat(int fd, const char *path, mode_t mode, int flag)
{
	if (!flag) return syscall(SYS_fchmodat, fd, path, mode, flag);

	if (flag != AT_SYMLINK_NOFOLLOW)
		return __syscall_ret(-EINVAL);

	struct stat st;
	int ret, fd2;
	char proc[15+3*sizeof(int)];

	if ((ret = __syscall(SYS_fstatat, fd, path, &st, flag)))
		return __syscall_ret(ret);
	if (S_ISLNK(st.st_mode))
		return __syscall_ret(-EOPNOTSUPP);

	if ((fd2 = __syscall(SYS_openat, fd, path, O_RDONLY|O_PATH|O_NOFOLLOW|O_NOCTTY)) < 0) {
		if (fd2 == -ELOOP)
			return __syscall_ret(-EOPNOTSUPP);
		return __syscall_ret(fd2);
	}

	__procfdname(proc, fd2);
	ret = __syscall(SYS_fstatat, AT_FDCWD, proc, &st, 0);
	if (!ret && !S_ISLNK(st.st_mode))
		ret = __syscall(SYS_fchmodat, AT_FDCWD, proc, mode);

	__syscall(SYS_close, fd2);
	return __syscall_ret(ret);
}


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

int __fxstat(int ver, int fd, struct stat *buf)
{
	return fstat(fd, buf);
}

int __fxstatat(int ver, int fd, const char *path, struct stat *buf, int flag)
{
	return fstatat(fd, path, buf, flag);
}

int __lxstat(int ver, const char *path, struct stat *buf)
{
	return lstat(path, buf);
}

int __xstat(int ver, const char *path, struct stat *buf)
{
	return stat(path, buf);
}

LFS64(__fxstat);
LFS64(__fxstatat);
LFS64(__lxstat);
LFS64(__xstat);

int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev)
{
	return mknod(path, mode, *dev);
}

int __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev)
{
	return mknodat(fd, path, mode, *dev);
}


text:
#include <wchar.h>

static const unsigned char table[] = {
#include "nonspacing.h"
};

static const unsigned char wtable[] = {
#include "wide.h"
};

int wcwidth(wchar_t wc)
{
	if (wc < 0xffU)
		return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0;
	if ((wc & 0xfffeffffU) < 0xfffe) {
		if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
			return 0;
		if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
			return 2;
		return 1;
	}
	if ((wc & 0xfffe) == 0xfffe)
		return -1;
	if (wc-0x20000U < 0x20000)
		return 2;
	if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100 < 0xef)
		return 0;
	return 1;
}


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

static const unsigned char table[] = {
#include "punct.h"
};

int iswpunct(wint_t wc)
{
	if (wc<0x20000U)
		return (table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1;
	return 0;
}

int __iswpunct_l(wint_t c, locale_t l)
{
	return iswpunct(c);
}

weak_alias(__iswpunct_l, iswpunct_l);


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

int toupper(int c)
{
	if (islower(c)) return c & 0x5f;
	return c;
}

int __toupper_l(int c, locale_t l)
{
	return toupper(c);
}

weak_alias(__toupper_l, toupper_l);


text:
#include <ctype.h>

/* nonsense function that should NEVER be used! */
int toascii(int c)
{
	return c & 0x7f;
}


text:
#include <ctype.h>
#include "libc.h"
#undef isprint

int isprint(int c)
{
	return (unsigned)c-0x20 < 0x5f;
}

int __isprint_l(int c, locale_t l)
{
	return isprint(c);
}

weak_alias(__isprint_l, isprint_l);


text:
#include <wchar.h>
#include <wctype.h>
#include "libc.h"

/* Our definition of whitespace is the Unicode White_Space property,
 * minus non-breaking spaces (U+00A0, U+2007, and U+202F) and script-
 * specific characters with non-blank glyphs (U+1680 and U+180E). */

int iswspace(wint_t wc)
{
	static const wchar_t spaces[] = {
		' ', '\t', '\n', '\r', 11, 12,  0x0085,
		0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
		0x2006, 0x2008, 0x2009, 0x200a,
		0x2028, 0x2029, 0x205f, 0x3000, 0
	};
	return wc && wcschr(spaces, wc);
}

int __iswspace_l(wint_t c, locale_t l)
{
	return iswspace(c);
}

weak_alias(__iswspace_l, iswspace_l);


text:
#include <ctype.h>
#include "libc.h"
#undef isalpha

int isalpha(int c)
{
	return ((unsigned)c|32)-'a' < 26;
}

int __isalpha_l(int c, locale_t l)
{
	return isalpha(c);
}

weak_alias(__isalpha_l, isalpha_l);


text:
#include <ctype.h>

int isascii(int c)
{
	return !(c&~0x7f);
}


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

#undef iswdigit

int iswdigit(wint_t wc)
{
	return (unsigned)wc-'0' < 10;
}

int __iswdigit_l(wint_t c, locale_t l)
{
	return iswdigit(c);
}

weak_alias(__iswdigit_l, iswdigit_l);


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

int iswalnum(wint_t wc)
{
	return iswdigit(wc) || iswalpha(wc);
}

int __iswalnum_l(wint_t c, locale_t l)
{
	return iswalnum(c);
}

weak_alias(__iswalnum_l, iswalnum_l);


text:
#include <endian.h>

#if __BYTE_ORDER == __BIG_ENDIAN
#define X(x) x
#else
#define X(x) (((x)/256 | (x)*256) % 65536)
#endif

static const unsigned short table[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),
X(0x200),X(0x320),X(0x220),X(0x220),X(0x220),X(0x220),X(0x200),X(0x200),
X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),
X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),X(0x200),
X(0x160),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),
X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),
X(0x8d8),X(0x8d8),X(0x8d8),X(0x8d8),X(0x8d8),X(0x8d8),X(0x8d8),X(0x8d8),
X(0x8d8),X(0x8d8),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),
X(0x4c0),X(0x8d5),X(0x8d5),X(0x8d5),X(0x8d5),X(0x8d5),X(0x8d5),X(0x8c5),
X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),
X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),X(0x8c5),
X(0x8c5),X(0x8c5),X(0x8c5),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),
X(0x4c0),X(0x8d6),X(0x8d6),X(0x8d6),X(0x8d6),X(0x8d6),X(0x8d6),X(0x8c6),
X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),
X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),X(0x8c6),
X(0x8c6),X(0x8c6),X(0x8c6),X(0x4c0),X(0x4c0),X(0x4c0),X(0x4c0),X(0x200),
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static const unsigned short *const ptable = table+128;

const unsigned short **__ctype_b_loc(void)
{
	return (void *)&ptable;
}


text:
#include <stddef.h>

size_t __ctype_get_mb_cur_max()
{
	return 4;
}


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

int iswupper(wint_t wc)
{
	return towlower(wc) != wc;
}

int __iswupper_l(wint_t c, locale_t l)
{
	return iswupper(c);
}

weak_alias(__iswupper_l, iswupper_l);


text:
#include <wctype.h>
#include <ctype.h>
#include "libc.h"

int iswblank(wint_t wc)
{
	return isblank(wc);
}

int __iswblank_l(wint_t c, locale_t l)
{
	return iswblank(c);
}

weak_alias(__iswblank_l, iswblank_l);


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

int iswlower(wint_t wc)
{
	return towupper(wc) != wc;
}

int __iswlower_l(wint_t c, locale_t l)
{
	return iswlower(c);
}

weak_alias(__iswlower_l, iswlower_l);


text:
#include <wchar.h>

int wcswidth(const wchar_t *wcs, size_t n)
{
	int l=0, k=0;
	for (; n-- && *wcs && (k = wcwidth(*wcs)) >= 0; l+=k, wcs++);
	return (k < 0) ? k : l;
}


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

int iswxdigit(wint_t wc)
{
	return (unsigned)(wc-'0') < 10 || (unsigned)((wc|32)-'a') < 6;
}

int __iswxdigit_l(wint_t c, locale_t l)
{
	return iswxdigit(c);
}

weak_alias(__iswxdigit_l, iswxdigit_l);


text:
#include <ctype.h>
#include "libc.h"
#undef islower

int islower(int c)
{
	return (unsigned)c-'a' < 26;
}

int __islower_l(int c, locale_t l)
{
	return islower(c);
}

weak_alias(__islower_l, islower_l);


text:
#include <stdint.h>

static const int32_t table[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
64,
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
91,92,93,94,95,96,
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
123,124,125,126,127,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static const int32_t *const ptable = table+128;

const int32_t **__ctype_tolower_loc(void)
{
	return (void *)&ptable;
}


text:
#include <wctype.h>
#include <string.h>
#include "libc.h"

#define WCTYPE_ALNUM  1
#define WCTYPE_ALPHA  2
#define WCTYPE_BLANK  3
#define WCTYPE_CNTRL  4
#define WCTYPE_DIGIT  5
#define WCTYPE_GRAPH  6
#define WCTYPE_LOWER  7
#define WCTYPE_PRINT  8
#define WCTYPE_PUNCT  9
#define WCTYPE_SPACE  10
#define WCTYPE_UPPER  11
#define WCTYPE_XDIGIT 12

int iswctype(wint_t wc, wctype_t type)
{
	switch (type) {
	case WCTYPE_ALNUM:
		return iswalnum(wc);
	case WCTYPE_ALPHA:
		return iswalpha(wc);
	case WCTYPE_BLANK:
		return iswblank(wc);
	case WCTYPE_CNTRL:
		return iswcntrl(wc);
	case WCTYPE_DIGIT:
		return iswdigit(wc);
	case WCTYPE_GRAPH:
		return iswgraph(wc);
	case WCTYPE_LOWER:
		return iswlower(wc);
	case WCTYPE_PRINT:
		return iswprint(wc);
	case WCTYPE_PUNCT:
		return iswpunct(wc);
	case WCTYPE_SPACE:
		return iswspace(wc);
	case WCTYPE_UPPER:
		return iswupper(wc);
	case WCTYPE_XDIGIT:
		return iswxdigit(wc);
	}
	return 0;
}

wctype_t wctype(const char *s)
{
	int i;
	const char *p;
	/* order must match! */
	static const char names[] =
		"alnum\0" "alpha\0" "blank\0"
		"cntrl\0" "digit\0" "graph\0"
		"lower\0" "print\0" "punct\0"
		"space\0" "upper\0" "xdigit";
	for (i=1, p=names; *p; i++, p+=6)
		if (*s == *p && !strcmp(s, p))
			return i;
	return 0;
}

int __iswctype_l(wint_t c, wctype_t t, locale_t l)
{
	return iswctype(c, t);
}

wctype_t __wctype_l(const char *s, locale_t l)
{
	return wctype(s);
}

weak_alias(__iswctype_l, iswctype_l);
weak_alias(__wctype_l, wctype_l);


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

static const unsigned char table[] = {
#include "alpha.h"
};

int iswalpha(wint_t wc)
{
	if (wc<0x20000U)
		return (table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1;
	if (wc<0x2fffeU)
		return 1;
	return 0;
}

int __iswalpha_l(wint_t c, locale_t l)
{
	return iswalpha(c);
}

weak_alias(__iswalpha_l, iswalpha_l);


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

int isxdigit(int c)
{
	return isdigit(c) || ((unsigned)c|32)-'a' < 6;
}

int __isxdigit_l(int c, locale_t l)
{
	return isxdigit(c);
}

weak_alias(__isxdigit_l, isxdigit_l);


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

/* Consider all legal codepoints as printable except for:
 * - C0 and C1 control characters
 * - U+2028 and U+2029 (line/para break)
 * - U+FFF9 through U+FFFB (interlinear annotation controls)
 * The following code is optimized heavily to make hot paths for the
 * expected printable characters. */

int iswprint(wint_t wc)
{
	if (wc < 0xffU)
		return (wc+1 & 0x7f) >= 0x21;
	if (wc < 0x2028U || wc-0x202aU < 0xd800-0x202a || wc-0xe000U < 0xfff9-0xe000)
		return 1;
	if (wc-0xfffcU > 0x10ffff-0xfffc || (wc&0xfffe)==0xfffe)
		return 0;
	return 1;
}

int __iswprint_l(wint_t c, locale_t l)
{
	return iswprint(c);
}

weak_alias(__iswprint_l, iswprint_l);


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

int iscntrl(int c)
{
	return (unsigned)c < 0x20 || c == 0x7f;
}

int __iscntrl_l(int c, locale_t l)
{
	return iscntrl(c);
}

weak_alias(__iscntrl_l, iscntrl_l);


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

int iswcntrl(wint_t wc)
{
	return (unsigned)wc < 32
	    || (unsigned)(wc-0x7f) < 33
	    || (unsigned)(wc-0x2028) < 2
	    || (unsigned)(wc-0xfff9) < 3;
}

int __iswcntrl_l(wint_t c, locale_t l)
{
	return iswcntrl(c);
}

weak_alias(__iswcntrl_l, iswcntrl_l);


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

int iswgraph(wint_t wc)
{
	/* ISO C defines this function as: */
	return !iswspace(wc) && iswprint(wc);
}

int __iswgraph_l(wint_t c, locale_t l)
{
	return iswgraph(c);
}

weak_alias(__iswgraph_l, iswgraph_l);


text:
#include <ctype.h>
#include "libc.h"
#undef isgraph

int isgraph(int c)
{
	return (unsigned)c-0x21 < 0x5e;
}

int __isgraph_l(int c, locale_t l)
{
	return isgraph(c);
}

weak_alias(__isgraph_l, isgraph_l);


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

int ispunct(int c)
{
	return isgraph(c) && !isalnum(c);
}

int __ispunct_l(int c, locale_t l)
{
	return ispunct(c);
}

weak_alias(__ispunct_l, ispunct_l);


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

int isblank(int c)
{
	return (c == ' ' || c == '\t');
}

int __isblank_l(int c, locale_t l)
{
	return isblank(c);
}

weak_alias(__isblank_l, isblank_l);


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

int isalnum(int c)
{
	return isalpha(c) || isdigit(c);
}

int __isalnum_l(int c, locale_t l)
{
	return isalnum(c);
}

weak_alias(__isalnum_l, isalnum_l);


text:
#include <ctype.h>
#include "libc.h"
#undef isupper

int isupper(int c)
{
	return (unsigned)c-'A' < 26;
}

int __isupper_l(int c, locale_t l)
{
	return isupper(c);
}

weak_alias(__isupper_l, isupper_l);


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

int tolower(int c)
{
	if (isupper(c)) return c | 32;
	return c;
}

int __tolower_l(int c, locale_t l)
{
	return tolower(c);
}

weak_alias(__tolower_l, tolower_l);


text:
#include <ctype.h>
#include "libc.h"
#undef isdigit

int isdigit(int c)
{
	return (unsigned)c-'0' < 10;
}

int __isdigit_l(int c, locale_t l)
{
	return isdigit(c);
}

weak_alias(__isdigit_l, isdigit_l);


text:
#include <stdint.h>

static const int32_t table[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
64,
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
91,92,93,94,95,96,
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
123,124,125,126,127,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static const int32_t *const ptable = table+128;

const int32_t **__ctype_toupper_loc(void)
{
	return (void *)&ptable;
}


text:
#include <ctype.h>
#include "libc.h"
#undef isspace

int isspace(int c)
{
	return c == ' ' || (unsigned)c-'\t' < 5;
}

int __isspace_l(int c, locale_t l)
{
	return isspace(c);
}

weak_alias(__isspace_l, isspace_l);


text:
#include <wctype.h>
#include <string.h>
#include "libc.h"

wctrans_t wctrans(const char *class)
{
	if (!strcmp(class, "toupper")) return (wctrans_t)1;
	if (!strcmp(class, "tolower")) return (wctrans_t)2;
	return 0;
}

wint_t towctrans(wint_t wc, wctrans_t trans)
{
	if (trans == (wctrans_t)1) return towupper(wc);
	if (trans == (wctrans_t)2) return towlower(wc);
	return wc;
}

wctrans_t __wctrans_l(const char *s, locale_t l)
{
	return wctrans(s);
}

wint_t __towctrans_l(wint_t c, wctrans_t t, locale_t l)
{
	return towctrans(c, t);
}

weak_alias(__wctrans_l, wctrans_l);
weak_alias(__towctrans_l, towctrans_l);


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

#define CASEMAP(u1,u2,l) { (u1), (l)-(u1), (u2)-(u1)+1 }
#define CASELACE(u1,u2) CASEMAP((u1),(u2),(u1)+1)

static const struct {
	unsigned short upper;
	signed char lower;
	unsigned char len;
} casemaps[] = {
	CASEMAP('A','Z','a'),
	CASEMAP(0xc0,0xde,0xe0),

	CASELACE(0x0100,0x012e),
	CASELACE(0x0132,0x0136),
	CASELACE(0x0139,0x0147),
	CASELACE(0x014a,0x0176),
	CASELACE(0x0179,0x017d),

	CASELACE(0x370,0x372),
	CASEMAP(0x391,0x3a1,0x3b1),
	CASEMAP(0x3a3,0x3ab,0x3c3),
	CASEMAP(0x400,0x40f,0x450),
	CASEMAP(0x410,0x42f,0x430),

	CASELACE(0x460,0x480),
	CASELACE(0x48a,0x4be),
	CASELACE(0x4c1,0x4cd),
	CASELACE(0x4d0,0x50e),

	CASELACE(0x514,0x526),
	CASEMAP(0x531,0x556,0x561),

	CASELACE(0x01a0,0x01a4),
	CASELACE(0x01b3,0x01b5),
	CASELACE(0x01cd,0x01db),
	CASELACE(0x01de,0x01ee),
	CASELACE(0x01f8,0x021e),
	CASELACE(0x0222,0x0232),
	CASELACE(0x03d8,0x03ee),

	CASELACE(0x1e00,0x1e94),
	CASELACE(0x1ea0,0x1efe),

	CASEMAP(0x1f08,0x1f0f,0x1f00),
	CASEMAP(0x1f18,0x1f1d,0x1f10),
	CASEMAP(0x1f28,0x1f2f,0x1f20),
	CASEMAP(0x1f38,0x1f3f,0x1f30),
	CASEMAP(0x1f48,0x1f4d,0x1f40),

	CASEMAP(0x1f68,0x1f6f,0x1f60),
	CASEMAP(0x1f88,0x1f8f,0x1f80),
	CASEMAP(0x1f98,0x1f9f,0x1f90),
	CASEMAP(0x1fa8,0x1faf,0x1fa0),
	CASEMAP(0x1fb8,0x1fb9,0x1fb0),
	CASEMAP(0x1fba,0x1fbb,0x1f70),
	CASEMAP(0x1fc8,0x1fcb,0x1f72),
	CASEMAP(0x1fd8,0x1fd9,0x1fd0),
	CASEMAP(0x1fda,0x1fdb,0x1f76),
	CASEMAP(0x1fe8,0x1fe9,0x1fe0),
	CASEMAP(0x1fea,0x1feb,0x1f7a),
	CASEMAP(0x1ff8,0x1ff9,0x1f78),
	CASEMAP(0x1ffa,0x1ffb,0x1f7c),

	CASELACE(0x246,0x24e),
	CASELACE(0x510,0x512),
	CASEMAP(0x2160,0x216f,0x2170),
	CASEMAP(0x2c00,0x2c2e,0x2c30),
	CASELACE(0x2c67,0x2c6b),
	CASELACE(0x2c80,0x2ce2),
	CASELACE(0x2ceb,0x2ced),

	CASELACE(0xa640,0xa66c),
	CASELACE(0xa680,0xa696),

	CASELACE(0xa722,0xa72e),
	CASELACE(0xa732,0xa76e),
	CASELACE(0xa779,0xa77b),
	CASELACE(0xa77e,0xa786),

	CASELACE(0xa790,0xa792),
	CASELACE(0xa7a0,0xa7a8),

	CASEMAP(0xff21,0xff3a,0xff41),
	{ 0,0,0 }
};

static const unsigned short pairs[][2] = {
	{ 'I',    0x0131 },
	{ 'S',    0x017f },
	{ 0x0130, 'i'    },
	{ 0x0178, 0x00ff },
	{ 0x0181, 0x0253 },
	{ 0x0182, 0x0183 },
	{ 0x0184, 0x0185 },
	{ 0x0186, 0x0254 },
	{ 0x0187, 0x0188 },
	{ 0x0189, 0x0256 },
	{ 0x018a, 0x0257 },
	{ 0x018b, 0x018c },
	{ 0x018e, 0x01dd },
	{ 0x018f, 0x0259 },
	{ 0x0190, 0x025b },
	{ 0x0191, 0x0192 },
	{ 0x0193, 0x0260 },
	{ 0x0194, 0x0263 },
	{ 0x0196, 0x0269 },
	{ 0x0197, 0x0268 },
	{ 0x0198, 0x0199 },
	{ 0x019c, 0x026f },
	{ 0x019d, 0x0272 },
	{ 0x019f, 0x0275 },
	{ 0x01a6, 0x0280 },
	{ 0x01a7, 0x01a8 },
	{ 0x01a9, 0x0283 },
	{ 0x01ac, 0x01ad },
	{ 0x01ae, 0x0288 },
	{ 0x01af, 0x01b0 },
	{ 0x01b1, 0x028a },
	{ 0x01b2, 0x028b },
	{ 0x01b7, 0x0292 },
	{ 0x01b8, 0x01b9 },
	{ 0x01bc, 0x01bd },
	{ 0x01c4, 0x01c6 },
	{ 0x01c4, 0x01c5 },
	{ 0x01c5, 0x01c6 },
	{ 0x01c7, 0x01c9 },
	{ 0x01c7, 0x01c8 },
	{ 0x01c8, 0x01c9 },
	{ 0x01ca, 0x01cc },
	{ 0x01ca, 0x01cb },
	{ 0x01cb, 0x01cc },
	{ 0x01f1, 0x01f3 },
	{ 0x01f1, 0x01f2 },
	{ 0x01f2, 0x01f3 },
	{ 0x01f4, 0x01f5 },
	{ 0x01f6, 0x0195 },
	{ 0x01f7, 0x01bf },
	{ 0x0220, 0x019e },
	{ 0x0386, 0x03ac },
	{ 0x0388, 0x03ad },
	{ 0x0389, 0x03ae },
	{ 0x038a, 0x03af },
	{ 0x038c, 0x03cc },
	{ 0x038e, 0x03cd },
	{ 0x038f, 0x03ce },
	{ 0x0399, 0x0345 },
	{ 0x0399, 0x1fbe },
	{ 0x03a3, 0x03c2 },
	{ 0x03f7, 0x03f8 },
	{ 0x03fa, 0x03fb },
	{ 0x1e60, 0x1e9b },
	{ 0x1e9e, 0xdf },

	{ 0x1f59, 0x1f51 },
	{ 0x1f5b, 0x1f53 },
	{ 0x1f5d, 0x1f55 },
	{ 0x1f5f, 0x1f57 },
	{ 0x1fbc, 0x1fb3 },
	{ 0x1fcc, 0x1fc3 },
	{ 0x1fec, 0x1fe5 },
	{ 0x1ffc, 0x1ff3 },

	{ 0x23a, 0x2c65 },
	{ 0x23b, 0x23c },
	{ 0x23d, 0x19a },
	{ 0x23e, 0x2c66 },
	{ 0x241, 0x242 },
	{ 0x243, 0x180 },
	{ 0x244, 0x289 },
	{ 0x245, 0x28c },
	{ 0x3f4, 0x3b8 },
	{ 0x3f9, 0x3f2 },
	{ 0x3fd, 0x37b },
	{ 0x3fe, 0x37c },
	{ 0x3ff, 0x37d },
	{ 0x4c0, 0x4cf },

	{ 0x2126, 0x3c9 },
	{ 0x212a, 'k' },
	{ 0x212b, 0xe5 },
	{ 0x2132, 0x214e },
	{ 0x2183, 0x2184 },
	{ 0x2c60, 0x2c61 },
	{ 0x2c62, 0x26b },
	{ 0x2c63, 0x1d7d },
	{ 0x2c64, 0x27d },
	{ 0x2c6d, 0x251 },
	{ 0x2c6e, 0x271 },
	{ 0x2c6f, 0x250 },
	{ 0x2c70, 0x252 },
	{ 0x2c72, 0x2c73 },
	{ 0x2c75, 0x2c76 },
	{ 0x2c7e, 0x23f },
	{ 0x2c7f, 0x240 },
	{ 0x2cf2, 0x2cf3 },

	{ 0xa77d, 0x1d79 },
	{ 0xa78b, 0xa78c },
	{ 0xa78d, 0x265 },
	{ 0xa7aa, 0x266 },

	{ 0x10c7, 0x2d27 },
	{ 0x10cd, 0x2d2d },

	/* bogus greek 'symbol' letters */
	{ 0x376, 0x377 },
	{ 0x39c, 0xb5 },
	{ 0x392, 0x3d0 },
	{ 0x398, 0x3d1 },
	{ 0x3a6, 0x3d5 },
	{ 0x3a0, 0x3d6 },
	{ 0x39a, 0x3f0 },
	{ 0x3a1, 0x3f1 },
	{ 0x395, 0x3f5 },
	{ 0x3cf, 0x3d7 },

	{ 0,0 }
};

static wchar_t __towcase(wchar_t wc, int lower)
{
	int i;
	int lmul = 2*lower-1;
	int lmask = lower-1;
	/* no letters with case in these large ranges */
	if (!iswalpha(wc)
	 || (unsigned)wc - 0x0600 <= 0x0fff-0x0600
	 || (unsigned)wc - 0x2e00 <= 0xa63f-0x2e00
	 || (unsigned)wc - 0xa800 <= 0xfeff-0xa800)
		return wc;
	/* special case because the diff between upper/lower is too big */
	if (lower && (unsigned)wc - 0x10a0 < 0x2e)
		if (wc>0x10c5 && wc != 0x10c7 && wc != 0x10cd) return wc;
		else return wc + 0x2d00 - 0x10a0;
	if (!lower && (unsigned)wc - 0x2d00 < 0x26)
		if (wc>0x2d25 && wc != 0x2d27 && wc != 0x2d2d) return wc;
		else return wc + 0x10a0 - 0x2d00;
	for (i=0; casemaps[i].len; i++) {
		int base = casemaps[i].upper + (lmask & casemaps[i].lower);
		if ((unsigned)wc-base < casemaps[i].len) {
			if (casemaps[i].lower == 1)
				return wc + lower - ((wc-casemaps[i].upper)&1);
			return wc + lmul*casemaps[i].lower;
		}
	}
	for (i=0; pairs[i][1-lower]; i++) {
		if (pairs[i][1-lower] == wc)
			return pairs[i][lower];
	}
	if ((unsigned)wc - (0x10428 - 0x28*lower) < 0x28)
		return wc - 0x28 + 0x50*lower;
	return wc;
}

wint_t towupper(wint_t wc)
{
	return __towcase(wc, 0);
}

wint_t towlower(wint_t wc)
{
	return __towcase(wc, 1);
}

wint_t __towupper_l(wint_t c, locale_t l)
{
	return towupper(c);
}

wint_t __towlower_l(wint_t c, locale_t l)
{
	return towlower(c);
}

weak_alias(__towupper_l, towupper_l);
weak_alias(__towlower_l, towlower_l);


text:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <elf.h>
#include <sys/mman.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <link.h>
#include <setjmp.h>
#include <pthread.h>
#include <ctype.h>
#include <dlfcn.h>
#include "pthread_impl.h"
#include "libc.h"

static int errflag;
static char errbuf[128];

#ifdef SHARED

#if ULONG_MAX == 0xffffffff
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Phdr Phdr;
typedef Elf32_Sym Sym;
#define R_TYPE(x) ((x)&255)
#define R_SYM(x) ((x)>>8)
#else
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Phdr Phdr;
typedef Elf64_Sym Sym;
#define R_TYPE(x) ((x)&0xffffffff)
#define R_SYM(x) ((x)>>32)
#endif

#define MAXP2(a,b) (-(-(a)&-(b)))
#define ALIGN(x,y) ((x)+(y)-1 & -(y))

struct debug {
	int ver;
	void *head;
	void (*bp)(void);
	int state;
	void *base;
};

struct td_index {
	size_t args[2];
	struct td_index *next;
};

struct dso {
	unsigned char *base;
	char *name;
	size_t *dynv;
	struct dso *next, *prev;

	Phdr *phdr;
	int phnum;
	size_t phentsize;
	int refcnt;
	Sym *syms;
	uint32_t *hashtab;
	uint32_t *ghashtab;
	int16_t *versym;
	char *strings;
	unsigned char *map;
	size_t map_len;
	dev_t dev;
	ino_t ino;
	signed char global;
	char relocated;
	char constructed;
	char kernel_mapped;
	struct dso **deps, *needed_by;
	char *rpath_orig, *rpath;
	void *tls_image;
	size_t tls_len, tls_size, tls_align, tls_id, tls_offset;
	size_t relro_start, relro_end;
	void **new_dtv;
	unsigned char *new_tls;
	int new_dtv_idx, new_tls_idx;
	struct td_index *td_index;
	struct dso *fini_next;
	char *shortname;
	char buf[];
};

struct symdef {
	Sym *sym;
	struct dso *dso;
};

enum {
	REL_ERR,
	REL_SYMBOLIC,
	REL_GOT,
	REL_PLT,
	REL_RELATIVE,
	REL_OFFSET,
	REL_OFFSET32,
	REL_COPY,
	REL_SYM_OR_REL,
	REL_TLS, /* everything past here is TLS */
	REL_DTPMOD,
	REL_DTPOFF,
	REL_TPOFF,
	REL_TPOFF_NEG,
	REL_TLSDESC,
};

#include "reloc.h"

int __init_tp(void *);
void __init_libc(char **, char *);

const char *__libc_get_version(void);

static struct dso *head, *tail, *ldso, *fini_head;
static char *env_path, *sys_path;
static unsigned long long gencnt;
static int runtime;
static int ldd_mode;
static int ldso_fail;
static int noload;
static jmp_buf *rtld_fail;
static pthread_rwlock_t lock;
static struct debug debug;
static size_t tls_cnt, tls_offset, tls_align = 4*sizeof(size_t);
static size_t static_tls_cnt;
static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];

struct debug *_dl_debug_addr = &debug;

#define AUX_CNT 38
#define DYN_CNT 34

static void decode_vec(size_t *v, size_t *a, size_t cnt)
{
	memset(a, 0, cnt*sizeof(size_t));
	for (; v[0]; v+=2) if (v[0]<cnt) {
		a[0] |= 1ULL<<v[0];
		a[v[0]] = v[1];
	}
}

static int search_vec(size_t *v, size_t *r, size_t key)
{
	for (; v[0]!=key; v+=2)
		if (!v[0]) return 0;
	*r = v[1];
	return 1;
}

static void error(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vsnprintf(errbuf, sizeof errbuf, fmt, ap);
	va_end(ap);
	if (runtime) longjmp(*rtld_fail, 1);
	dprintf(2, "%s\n", errbuf);
	ldso_fail = 1;
}

static uint32_t sysv_hash(const char *s0)
{
	const unsigned char *s = (void *)s0;
	uint_fast32_t h = 0;
	while (*s) {
		h = 16*h + *s++;
		h ^= h>>24 & 0xf0;
	}
	return h & 0xfffffff;
}

static uint32_t gnu_hash(const char *s0)
{
	const unsigned char *s = (void *)s0;
	uint_fast32_t h = 5381;
	for (; *s; s++)
		h = h*33 + *s;
	return h;
}

static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
{
	size_t i;
	Sym *syms = dso->syms;
	uint32_t *hashtab = dso->hashtab;
	char *strings = dso->strings;
	for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) {
		if ((!dso->versym || dso->versym[i] >= 0)
		    && (!strcmp(s, strings+syms[i].st_name)))
			return syms+i;
	}
	return 0;
}

static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
{
	Sym *syms = dso->syms;
	char *strings = dso->strings;
	uint32_t *hashtab = dso->ghashtab;
	uint32_t nbuckets = hashtab[0];
	uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
	uint32_t h2;
	uint32_t *hashval;
	uint32_t i = buckets[h1 % nbuckets];

	if (!i) return 0;

	hashval = buckets + nbuckets + (i - hashtab[1]);

	for (h1 |= 1; ; i++) {
		h2 = *hashval++;
		if ((!dso->versym || dso->versym[i] >= 0)
		    && (h1 == (h2|1)) && !strcmp(s, strings + syms[i].st_name))
			return syms+i;
		if (h2 & 1) break;
	}

	return 0;
}

#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON | 1<<STT_TLS)
#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)

#ifndef ARCH_SYM_REJECT_UND
#define ARCH_SYM_REJECT_UND(s) 0
#endif

static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
{
	uint32_t h = 0, gh = 0;
	struct symdef def = {0};
	for (; dso; dso=dso->next) {
		Sym *sym;
		if (!dso->global) continue;
		if (dso->ghashtab) {
			if (!gh) gh = gnu_hash(s);
			sym = gnu_lookup(s, gh, dso);
		} else {
			if (!h) h = sysv_hash(s);
			sym = sysv_lookup(s, h, dso);
		}
		if (!sym) continue;
		if (!sym->st_shndx)
			if (need_def || (sym->st_info&0xf) == STT_TLS
			    || ARCH_SYM_REJECT_UND(sym))
				continue;
		if (!sym->st_value)
			if ((sym->st_info&0xf) != STT_TLS)
				continue;
		if (!(1<<(sym->st_info&0xf) & OK_TYPES)) continue;
		if (!(1<<(sym->st_info>>4) & OK_BINDS)) continue;

		if (def.sym && sym->st_info>>4 == STB_WEAK) continue;
		def.sym = sym;
		def.dso = dso;
		if (sym->st_info>>4 == STB_GLOBAL) break;
	}
	return def;
}

#define NO_INLINE_ADDEND (1<<REL_COPY | 1<<REL_GOT | 1<<REL_PLT)

ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();

static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
{
	unsigned char *base = dso->base;
	Sym *syms = dso->syms;
	char *strings = dso->strings;
	Sym *sym;
	const char *name;
	void *ctx;
	int astype, type;
	int sym_index;
	struct symdef def;
	size_t *reloc_addr;
	size_t sym_val;
	size_t tls_val;
	size_t addend;

	for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
		astype = R_TYPE(rel[1]);
		if (!astype) continue;
		type = remap_rel(astype);
		if (!type) {
			error("Error relocating %s: unsupported relocation type %d",
				dso->name, astype);
			continue;
		}
		sym_index = R_SYM(rel[1]);
		reloc_addr = (void *)(base + rel[0]);
		if (sym_index) {
			sym = syms + sym_index;
			name = strings + sym->st_name;
			ctx = type==REL_COPY ? head->next : head;
			def = find_sym(ctx, name, type==REL_PLT);
			if (!def.sym && (sym->st_shndx != SHN_UNDEF
			    || sym->st_info>>4 != STB_WEAK)) {
				error("Error relocating %s: %s: symbol not found",
					dso->name, name);
				continue;
			}
		} else {
			sym = 0;
			def.sym = 0;
			def.dso = dso;
		}

		addend = stride>2 ? rel[2]
			: (1<<type & NO_INLINE_ADDEND) ? 0
			: *reloc_addr;

		sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0;
		tls_val = def.sym ? def.sym->st_value : 0;

		switch(type) {
		case REL_OFFSET:
			addend -= (size_t)reloc_addr;
		case REL_SYMBOLIC:
		case REL_GOT:
		case REL_PLT:
			*reloc_addr = sym_val + addend;
			break;
		case REL_RELATIVE:
			*reloc_addr = (size_t)base + addend;
			break;
		case REL_SYM_OR_REL:
			if (sym) *reloc_addr = sym_val + addend;
			else *reloc_addr = (size_t)base + addend;
			break;
		case REL_COPY:
			memcpy(reloc_addr, (void *)sym_val, sym->st_size);
			break;
		case REL_OFFSET32:
			*(uint32_t *)reloc_addr = sym_val + addend
				- (size_t)reloc_addr;
			break;
		case REL_DTPMOD:
			*reloc_addr = def.dso->tls_id;
			break;
		case REL_DTPOFF:
			*reloc_addr = tls_val + addend;
			break;
#ifdef TLS_ABOVE_TP
		case REL_TPOFF:
			*reloc_addr = tls_val + def.dso->tls_offset + TPOFF_K + addend;
			break;
#else
		case REL_TPOFF:
			*reloc_addr = tls_val - def.dso->tls_offset + addend;
			break;
		case REL_TPOFF_NEG:
			*reloc_addr = def.dso->tls_offset - tls_val + addend;
			break;
#endif
		case REL_TLSDESC:
			if (stride<3) addend = reloc_addr[1];
			if (runtime && def.dso->tls_id >= static_tls_cnt) {
				struct td_index *new = malloc(sizeof *new);
				if (!new) error(
					"Error relocating %s: cannot allocate TLSDESC for %s",
					dso->name, sym ? name : "(local)" );
				new->next = dso->td_index;
				dso->td_index = new;
				new->args[0] = def.dso->tls_id;
				new->args[1] = tls_val + addend;
				reloc_addr[0] = (size_t)__tlsdesc_dynamic;
				reloc_addr[1] = (size_t)new;
			} else {
				reloc_addr[0] = (size_t)__tlsdesc_static;
#ifdef TLS_ABOVE_TP
				reloc_addr[1] = tls_val + def.dso->tls_offset
					+ TPOFF_K + addend;
#else
				reloc_addr[1] = tls_val - def.dso->tls_offset
					+ addend;
#endif
			}
			break;
		}
	}
}

/* A huge hack: to make up for the wastefulness of shared libraries
 * needing at least a page of dirty memory even if they have no global
 * data, we reclaim the gaps at the beginning and end of writable maps
 * and "donate" them to the heap by setting up minimal malloc
 * structures and then freeing them. */

static void reclaim(struct dso *dso, size_t start, size_t end)
{
	size_t *a, *z;
	if (start >= dso->relro_start && start < dso->relro_end) start = dso->relro_end;
	if (end   >= dso->relro_start && end   < dso->relro_end) end = dso->relro_start;
	start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t);
	end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t);
	if (start>end || end-start < 4*sizeof(size_t)) return;
	a = (size_t *)(dso->base + start);
	z = (size_t *)(dso->base + end);
	a[-2] = 1;
	a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1;
	z[1] = 1;
	free(a);
}

static void reclaim_gaps(struct dso *dso)
{
	Phdr *ph = dso->phdr;
	size_t phcnt = dso->phnum;

	for (; phcnt--; ph=(void *)((char *)ph+dso->phentsize)) {
		if (ph->p_type!=PT_LOAD) continue;
		if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue;
		reclaim(dso, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr);
		reclaim(dso, ph->p_vaddr+ph->p_memsz,
			ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE);
	}
}

static void *map_library(int fd, struct dso *dso)
{
	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
	void *allocated_buf=0;
	size_t phsize;
	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
	size_t this_min, this_max;
	off_t off_start;
	Ehdr *eh;
	Phdr *ph, *ph0;
	unsigned prot;
	unsigned char *map=MAP_FAILED, *base;
	size_t dyn=0;
	size_t tls_image=0;
	size_t i;

	ssize_t l = read(fd, buf, sizeof buf);
	eh = buf;
	if (l<0) return 0;
	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
		goto noexec;
	phsize = eh->e_phentsize * eh->e_phnum;
	if (phsize > sizeof buf - sizeof *eh) {
		allocated_buf = malloc(phsize);
		if (!allocated_buf) return 0;
		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
		if (l < 0) goto error;
		if (l != phsize) goto noexec;
		ph = ph0 = allocated_buf;
	} else if (eh->e_phoff + phsize > l) {
		l = pread(fd, buf+1, phsize, eh->e_phoff);
		if (l < 0) goto error;
		if (l != phsize) goto noexec;
		ph = ph0 = (void *)(buf + 1);
	} else {
		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
	}
	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
		if (ph->p_type == PT_DYNAMIC) {
			dyn = ph->p_vaddr;
		} else if (ph->p_type == PT_TLS) {
			tls_image = ph->p_vaddr;
			dso->tls_align = ph->p_align;
			dso->tls_len = ph->p_filesz;
			dso->tls_size = ph->p_memsz;
		} else if (ph->p_type == PT_GNU_RELRO) {
			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
		}
		if (ph->p_type != PT_LOAD) continue;
		if (ph->p_vaddr < addr_min) {
			addr_min = ph->p_vaddr;
			off_start = ph->p_offset;
			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
		}
		if (ph->p_vaddr+ph->p_memsz > addr_max) {
			addr_max = ph->p_vaddr+ph->p_memsz;
		}
	}
	if (!dyn) goto noexec;
	addr_max += PAGE_SIZE-1;
	addr_max &= -PAGE_SIZE;
	addr_min &= -PAGE_SIZE;
	off_start &= -PAGE_SIZE;
	map_len = addr_max - addr_min + off_start;
	/* The first time, we map too much, possibly even more than
	 * the length of the file. This is okay because we will not
	 * use the invalid part; we just need to reserve the right
	 * amount of virtual address space to map over later. */
	map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
	if (map==MAP_FAILED) goto error;
	/* If the loaded file is not relocatable and the requested address is
	 * not available, then the load operation must fail. */
	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
		errno = EBUSY;
		goto error;
	}
	base = map - addr_min;
	dso->phdr = 0;
	dso->phnum = 0;
	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
		if (ph->p_type != PT_LOAD) continue;
		/* Check if the programs headers are in this load segment, and
		 * if so, record the address for use by dl_iterate_phdr. */
		if (!dso->phdr && eh->e_phoff >= ph->p_offset
		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
			dso->phdr = (void *)(base + ph->p_vaddr
				+ (eh->e_phoff-ph->p_offset));
			dso->phnum = eh->e_phnum;
			dso->phentsize = eh->e_phentsize;
		}
		/* Reuse the existing mapping for the lowest-address LOAD */
		if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue;
		this_min = ph->p_vaddr & -PAGE_SIZE;
		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
		off_start = ph->p_offset & -PAGE_SIZE;
		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
		if (mmap(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
			goto error;
		if (ph->p_memsz > ph->p_filesz) {
			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
			if (pgbrk-(size_t)base < this_max && mmap((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
				goto error;
		}
	}
	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC) < 0)
				goto error;
			break;
		}
	dso->map = map;
	dso->map_len = map_len;
	dso->base = base;
	dso->dynv = (void *)(base+dyn);
	if (dso->tls_size) dso->tls_image = (void *)(base+tls_image);
	if (!runtime) reclaim_gaps(dso);
	free(allocated_buf);
	return map;
noexec:
	errno = ENOEXEC;
error:
	if (map!=MAP_FAILED) munmap(map, map_len);
	free(allocated_buf);
	return 0;
}

static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
{
	size_t l;
	int fd;
	for (;;) {
		s += strspn(s, ":\n");
		l = strcspn(s, ":\n");
		if (l-1 >= INT_MAX) return -1;
		if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) >= buf_size)
			continue;
		if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd;
		s += l;
	}
}

static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)
{
	size_t n, l;
	const char *s, *t, *origin;
	char *d;
	if (p->rpath) return 0;
	if (!p->rpath_orig) return -1;
	if (!strchr(p->rpath_orig, '$')) {
		p->rpath = p->rpath_orig;
		return 0;
	}
	n = 0;
	s = p->rpath_orig;
	while ((t=strchr(s, '$'))) {
		if (strncmp(t, "$ORIGIN", 7) && strncmp(t, "${ORIGIN}", 9))
			return -1;
		s = t+1;
		n++;
	}
	if (n > SSIZE_MAX/PATH_MAX) return -1;

	if (p->kernel_mapped) {
		/* $ORIGIN searches cannot be performed for the main program
		 * when it is suid/sgid/AT_SECURE. This is because the
		 * pathname is under the control of the caller of execve.
		 * For libraries, however, $ORIGIN can be processed safely
		 * since the library's pathname came from a trusted source
		 * (either system paths or a call to dlopen). */
		if (libc.secure)
			return -1;
		l = readlink("/proc/self/exe", buf, buf_size);
		if (l >= buf_size)
			return -1;
		buf[l] = 0;
		origin = buf;
	} else {
		origin = p->name;
	}
	t = strrchr(origin, '/');
	l = t ? t-origin : 0;
	p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1);
	if (!p->rpath) return -1;

	d = p->rpath;
	s = p->rpath_orig;
	while ((t=strchr(s, '$'))) {
		memcpy(d, s, t-s);
		d += t-s;
		memcpy(d, origin, l);
		d += l;
		/* It was determined previously that the '$' is followed
		 * either by "ORIGIN" or "{ORIGIN}". */
		s = t + 7 + 2*(t[1]=='{');
	}
	strcpy(d, s);
	return 0;
}

static void decode_dyn(struct dso *p)
{
	size_t dyn[DYN_CNT] = {0};
	decode_vec(p->dynv, dyn, DYN_CNT);
	p->syms = (void *)(p->base + dyn[DT_SYMTAB]);
	p->strings = (void *)(p->base + dyn[DT_STRTAB]);
	if (dyn[0]&(1<<DT_HASH))
		p->hashtab = (void *)(p->base + dyn[DT_HASH]);
	if (dyn[0]&(1<<DT_RPATH))
		p->rpath_orig = (void *)(p->strings + dyn[DT_RPATH]);
	if (search_vec(p->dynv, dyn, DT_GNU_HASH))
		p->ghashtab = (void *)(p->base + *dyn);
	if (search_vec(p->dynv, dyn, DT_VERSYM))
		p->versym = (void *)(p->base + *dyn);
}

static struct dso *load_library(const char *name, struct dso *needed_by)
{
	char buf[2*NAME_MAX+2];
	const char *pathname;
	unsigned char *map;
	struct dso *p, temp_dso = {0};
	int fd;
	struct stat st;
	size_t alloc_size;
	int n_th = 0;
	int is_self = 0;

	if (!*name) {
		errno = EINVAL;
		return 0;
	}

	/* Catch and block attempts to reload the implementation itself */
	if (name[0]=='l' && name[1]=='i' && name[2]=='b') {
		static const char *rp, reserved[] =
			"c\0pthread\0rt\0m\0dl\0util\0xnet\0";
		char *z = strchr(name, '.');
		if (z) {
			size_t l = z-name;
			for (rp=reserved; *rp && strncmp(name+3, rp, l-3); rp+=strlen(rp)+1);
			if (*rp) {
				if (ldd_mode) {
					/* Track which names have been resolved
					 * and only report each one once. */
					static unsigned reported;
					unsigned mask = 1U<<(rp-reserved);
					if (!(reported & mask)) {
						reported |= mask;
						dprintf(1, "\t%s => %s (%p)\n",
							name, ldso->name,
							ldso->base);
					}
				}
				is_self = 1;
			}
		}
	}
	if (!strcmp(name, ldso->name)) is_self = 1;
	if (is_self) {
		if (!ldso->prev) {
			tail->next = ldso;
			ldso->prev = tail;
			tail = ldso->next ? ldso->next : ldso;
		}
		return ldso;
	}
	if (strchr(name, '/')) {
		pathname = name;
		fd = open(name, O_RDONLY|O_CLOEXEC);
	} else {
		/* Search for the name to see if it's already loaded */
		for (p=head->next; p; p=p->next) {
			if (p->shortname && !strcmp(p->shortname, name)) {
				p->refcnt++;
				return p;
			}
		}
		if (strlen(name) > NAME_MAX) return 0;
		fd = -1;
		if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
		for (p=needed_by; fd < 0 && p; p=p->needed_by)
			if (!fixup_rpath(p, buf, sizeof buf))
				fd = path_open(name, p->rpath, buf, sizeof buf);
		if (fd < 0) {
			if (!sys_path) {
				char *prefix = 0;
				size_t prefix_len;
				if (ldso->name[0]=='/') {
					char *s, *t, *z;
					for (s=t=z=ldso->name; *s; s++)
						if (*s=='/') z=t, t=s;
					prefix_len = z-ldso->name;
					if (prefix_len < PATH_MAX)
						prefix = ldso->name;
				}
				if (!prefix) {
					prefix = "";
					prefix_len = 0;
				}
				char etc_ldso_path[prefix_len + 1
					+ sizeof "/etc/ld-musl-" LDSO_ARCH ".path"];
				snprintf(etc_ldso_path, sizeof etc_ldso_path,
					"%.*s/etc/ld-musl-" LDSO_ARCH ".path",
					(int)prefix_len, prefix);
				FILE *f = fopen(etc_ldso_path, "rbe");
				if (f) {
					if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) {
						free(sys_path);
						sys_path = "";
					}
					fclose(f);
				} else if (errno != ENOENT) {
					sys_path = "";
				}
			}
			if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
			fd = path_open(name, sys_path, buf, sizeof buf);
		}
		pathname = buf;
	}
	if (fd < 0) return 0;
	if (fstat(fd, &st) < 0) {
		close(fd);
		return 0;
	}
	for (p=head->next; p; p=p->next) {
		if (p->dev == st.st_dev && p->ino == st.st_ino) {
			/* If this library was previously loaded with a
			 * pathname but a search found the same inode,
			 * setup its shortname so it can be found by name. */
			if (!p->shortname && pathname != name)
				p->shortname = strrchr(p->name, '/')+1;
			close(fd);
			p->refcnt++;
			return p;
		}
	}
	map = noload ? 0 : map_library(fd, &temp_dso);
	close(fd);
	if (!map) return 0;

	/* Allocate storage for the new DSO. When there is TLS, this
	 * storage must include a reservation for all pre-existing
	 * threads to obtain copies of both the new TLS, and an
	 * extended DTV capable of storing an additional slot for
	 * the newly-loaded DSO. */
	alloc_size = sizeof *p + strlen(pathname) + 1;
	if (runtime && temp_dso.tls_image) {
		size_t per_th = temp_dso.tls_size + temp_dso.tls_align
			+ sizeof(void *) * (tls_cnt+3);
		n_th = libc.threads_minus_1 + 1;
		if (n_th > SSIZE_MAX / per_th) alloc_size = SIZE_MAX;
		else alloc_size += n_th * per_th;
	}
	p = calloc(1, alloc_size);
	if (!p) {
		munmap(map, temp_dso.map_len);
		return 0;
	}
	memcpy(p, &temp_dso, sizeof temp_dso);
	decode_dyn(p);
	p->dev = st.st_dev;
	p->ino = st.st_ino;
	p->refcnt = 1;
	p->needed_by = needed_by;
	p->name = p->buf;
	strcpy(p->name, pathname);
	/* Add a shortname only if name arg was not an explicit pathname. */
	if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
	if (p->tls_image) {
		if (runtime && !libc.has_thread_pointer) {
			munmap(map, p->map_len);
			free(p);
			errno = ENOSYS;
			return 0;
		}
		p->tls_id = ++tls_cnt;
		tls_align = MAXP2(tls_align, p->tls_align);
#ifdef TLS_ABOVE_TP
		p->tls_offset = tls_offset + ( (tls_align-1) &
			-(tls_offset + (uintptr_t)p->tls_image) );
		tls_offset += p->tls_size;
#else
		tls_offset += p->tls_size + p->tls_align - 1;
		tls_offset -= (tls_offset + (uintptr_t)p->tls_image)
			& (p->tls_align-1);
		p->tls_offset = tls_offset;
#endif
		p->new_dtv = (void *)(-sizeof(size_t) &
			(uintptr_t)(p->name+strlen(p->name)+sizeof(size_t)));
		p->new_tls = (void *)(p->new_dtv + n_th*(tls_cnt+1));
	}

	tail->next = p;
	p->prev = tail;
	tail = p;

	if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, p->base);

	return p;
}

static void load_deps(struct dso *p)
{
	size_t i, ndeps=0;
	struct dso ***deps = &p->deps, **tmp, *dep;
	for (; p; p=p->next) {
		for (i=0; p->dynv[i]; i+=2) {
			if (p->dynv[i] != DT_NEEDED) continue;
			dep = load_library(p->strings + p->dynv[i+1], p);
			if (!dep) {
				error("Error loading shared library %s: %m (needed by %s)",
					p->strings + p->dynv[i+1], p->name);
				continue;
			}
			if (runtime) {
				tmp = realloc(*deps, sizeof(*tmp)*(ndeps+2));
				if (!tmp) longjmp(*rtld_fail, 1);
				tmp[ndeps++] = dep;
				tmp[ndeps] = 0;
				*deps = tmp;
			}
		}
	}
}

static void load_preload(char *s)
{
	int tmp;
	char *z;
	for (z=s; *z; s=z) {
		for (   ; *s && (isspace(*s) || *s==':'); s++);
		for (z=s; *z && !isspace(*z) && *z!=':'; z++);
		tmp = *z;
		*z = 0;
		load_library(s, 0);
		*z = tmp;
	}
}

static void make_global(struct dso *p)
{
	for (; p; p=p->next) p->global = 1;
}

static void reloc_all(struct dso *p)
{
	size_t dyn[DYN_CNT] = {0};
	for (; p; p=p->next) {
		if (p->relocated) continue;
		decode_vec(p->dynv, dyn, DYN_CNT);
#ifdef NEED_ARCH_RELOCS
		do_arch_relocs(p, head);
#endif
		do_relocs(p, (void *)(p->base+dyn[DT_JMPREL]), dyn[DT_PLTRELSZ],
			2+(dyn[DT_PLTREL]==DT_RELA));
		do_relocs(p, (void *)(p->base+dyn[DT_REL]), dyn[DT_RELSZ], 2);
		do_relocs(p, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], 3);

		if (p->relro_start != p->relro_end &&
		    mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
			error("Error relocating %s: RELRO protection failed: %m",
				p->name);
		}

		p->relocated = 1;
	}
}

static void kernel_mapped_dso(struct dso *p)
{
	size_t min_addr = -1, max_addr = 0, cnt;
	Phdr *ph = p->phdr;
	for (cnt = p->phnum; cnt--; ph = (void *)((char *)ph + p->phentsize)) {
		if (ph->p_type == PT_DYNAMIC) {
			p->dynv = (void *)(p->base + ph->p_vaddr);
		} else if (ph->p_type == PT_GNU_RELRO) {
			p->relro_start = ph->p_vaddr & -PAGE_SIZE;
			p->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
		}
		if (ph->p_type != PT_LOAD) continue;
		if (ph->p_vaddr < min_addr)
			min_addr = ph->p_vaddr;
		if (ph->p_vaddr+ph->p_memsz > max_addr)
			max_addr = ph->p_vaddr+ph->p_memsz;
	}
	min_addr &= -PAGE_SIZE;
	max_addr = (max_addr + PAGE_SIZE-1) & -PAGE_SIZE;
	p->map = p->base + min_addr;
	p->map_len = max_addr - min_addr;
	p->kernel_mapped = 1;
}

static void do_fini()
{
	struct dso *p;
	size_t dyn[DYN_CNT] = {0};
	for (p=fini_head; p; p=p->fini_next) {
		if (!p->constructed) continue;
		decode_vec(p->dynv, dyn, DYN_CNT);
		if (dyn[0] & (1<<DT_FINI_ARRAY)) {
			size_t n = dyn[DT_FINI_ARRAYSZ]/sizeof(size_t);
			size_t *fn = (size_t *)(p->base + dyn[DT_FINI_ARRAY])+n;
			while (n--) ((void (*)(void))*--fn)();
		}
#ifndef NO_LEGACY_INITFINI
		if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
			((void (*)(void))(p->base + dyn[DT_FINI]))();
#endif
	}
}

static void do_init_fini(struct dso *p)
{
	size_t dyn[DYN_CNT] = {0};
	int need_locking = libc.threads_minus_1;
	/* Allow recursive calls that arise when a library calls
	 * dlopen from one of its constructors, but block any
	 * other threads until all ctors have finished. */
	if (need_locking) pthread_mutex_lock(&init_fini_lock);
	for (; p; p=p->prev) {
		if (p->constructed) continue;
		p->constructed = 1;
		decode_vec(p->dynv, dyn, DYN_CNT);
		if (dyn[0] & ((1<<DT_FINI) | (1<<DT_FINI_ARRAY))) {
			p->fini_next = fini_head;
			fini_head = p;
		}
#ifndef NO_LEGACY_INITFINI
		if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT])
			((void (*)(void))(p->base + dyn[DT_INIT]))();
#endif
		if (dyn[0] & (1<<DT_INIT_ARRAY)) {
			size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t);
			size_t *fn = (void *)(p->base + dyn[DT_INIT_ARRAY]);
			while (n--) ((void (*)(void))*fn++)();
		}
		if (!need_locking && libc.threads_minus_1) {
			need_locking = 1;
			pthread_mutex_lock(&init_fini_lock);
		}
	}
	if (need_locking) pthread_mutex_unlock(&init_fini_lock);
}

void _dl_debug_state(void)
{
}

void __reset_tls()
{
	pthread_t self = __pthread_self();
	struct dso *p;
	for (p=head; p; p=p->next) {
		if (!p->tls_id || !self->dtv[p->tls_id]) continue;
		memcpy(self->dtv[p->tls_id], p->tls_image, p->tls_len);
		memset((char *)self->dtv[p->tls_id]+p->tls_len, 0,
			p->tls_size - p->tls_len);
		if (p->tls_id == (size_t)self->dtv[0]) break;
	}
}

void *__copy_tls(unsigned char *mem)
{
	pthread_t td;
	struct dso *p;

	void **dtv = (void *)mem;
	dtv[0] = (void *)tls_cnt;
	if (!tls_cnt) {
		td = (void *)(dtv+1);
		td->dtv = dtv;
		return td;
	}

#ifdef TLS_ABOVE_TP
	mem += sizeof(void *) * (tls_cnt+1);
	mem += -((uintptr_t)mem + sizeof(struct pthread)) & (tls_align-1);
	td = (pthread_t)mem;
	mem += sizeof(struct pthread);

	for (p=head; p; p=p->next) {
		if (!p->tls_id) continue;
		dtv[p->tls_id] = mem + p->tls_offset;
		memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
	}
#else
	mem += libc.tls_size - sizeof(struct pthread);
	mem -= (uintptr_t)mem & (tls_align-1);
	td = (pthread_t)mem;

	for (p=head; p; p=p->next) {
		if (!p->tls_id) continue;
		dtv[p->tls_id] = mem - p->tls_offset;
		memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
	}
#endif
	td->dtv = dtv;
	return td;
}

void *__tls_get_new(size_t *v)
{
	pthread_t self = __pthread_self();

	/* Block signals to make accessing new TLS async-signal-safe */
	sigset_t set;
	__block_all_sigs(&set);
	if (v[0]<=(size_t)self->dtv[0]) {
		__restore_sigs(&set);
		return (char *)self->dtv[v[0]]+v[1];
	}

	/* This is safe without any locks held because, if the caller
	 * is able to request the Nth entry of the DTV, the DSO list
	 * must be valid at least that far out and it was synchronized
	 * at program startup or by an already-completed call to dlopen. */
	struct dso *p;
	for (p=head; p->tls_id != v[0]; p=p->next);

	/* Get new DTV space from new DSO if needed */
	if (v[0] > (size_t)self->dtv[0]) {
		void **newdtv = p->new_dtv +
			(v[0]+1)*sizeof(void *)*a_fetch_add(&p->new_dtv_idx,1);
		memcpy(newdtv, self->dtv,
			((size_t)self->dtv[0]+1) * sizeof(void *));
		newdtv[0] = (void *)v[0];
		self->dtv = newdtv;
	}

	/* Get new TLS memory from all new DSOs up to the requested one */
	unsigned char *mem;
	for (p=head; ; p=p->next) {
		if (!p->tls_id || self->dtv[p->tls_id]) continue;
		mem = p->new_tls + (p->tls_size + p->tls_align)
			* a_fetch_add(&p->new_tls_idx,1);
		mem += ((uintptr_t)p->tls_image - (uintptr_t)mem)
			& (p->tls_align-1);
		self->dtv[p->tls_id] = mem;
		memcpy(mem, p->tls_image, p->tls_len);
		if (p->tls_id == v[0]) break;
	}
	__restore_sigs(&set);
	return mem + v[1];
}

static void update_tls_size()
{
	libc.tls_size = ALIGN(
		(1+tls_cnt) * sizeof(void *) +
		tls_offset +
		sizeof(struct pthread) +
		tls_align * 2,
	tls_align);
}

void *__dynlink(int argc, char **argv)
{
	size_t aux[AUX_CNT] = {0};
	size_t i;
	Phdr *phdr;
	Ehdr *ehdr;
	static struct dso builtin_dsos[3];
	struct dso *const app = builtin_dsos+0;
	struct dso *const lib = builtin_dsos+1;
	struct dso *const vdso = builtin_dsos+2;
	char *env_preload=0;
	size_t vdso_base;
	size_t *auxv;
	char **envp = argv+argc+1;
	void *initial_tls;

	/* Find aux vector just past environ[] */
	for (i=argc+1; argv[i]; i++)
		if (!memcmp(argv[i], "LD_LIBRARY_PATH=", 16))
			env_path = argv[i]+16;
		else if (!memcmp(argv[i], "LD_PRELOAD=", 11))
			env_preload = argv[i]+11;
	auxv = (void *)(argv+i+1);

	decode_vec(auxv, aux, AUX_CNT);

	/* Only trust user/env if kernel says we're not suid/sgid */
	if ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID]
	  || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {
		env_path = 0;
		env_preload = 0;
		libc.secure = 1;
	}
	libc.page_size = aux[AT_PAGESZ];

	/* If the dynamic linker was invoked as a program itself, AT_BASE
	 * will not be set. In that case, we assume the base address is
	 * the start of the page containing the PHDRs; I don't know any
	 * better approach... */
	if (!aux[AT_BASE]) {
		aux[AT_BASE] = aux[AT_PHDR] & -PAGE_SIZE;
		aux[AT_PHDR] = aux[AT_PHENT] = aux[AT_PHNUM] = 0;
	}

	/* The dynamic linker load address is passed by the kernel
	 * in the AUX vector, so this is easy. */
	lib->base = (void *)aux[AT_BASE];
	lib->name = lib->shortname = "libc.so";
	lib->global = 1;
	ehdr = (void *)lib->base;
	lib->phnum = ehdr->e_phnum;
	lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff);
	lib->phentsize = ehdr->e_phentsize;
	kernel_mapped_dso(lib);
	decode_dyn(lib);

	if (aux[AT_PHDR]) {
		size_t interp_off = 0;
		size_t tls_image = 0;
		/* Find load address of the main program, via AT_PHDR vs PT_PHDR. */
		app->phdr = phdr = (void *)aux[AT_PHDR];
		app->phnum = aux[AT_PHNUM];
		app->phentsize = aux[AT_PHENT];
		for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) {
			if (phdr->p_type == PT_PHDR)
				app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr);
			else if (phdr->p_type == PT_INTERP)
				interp_off = (size_t)phdr->p_vaddr;
			else if (phdr->p_type == PT_TLS) {
				tls_image = phdr->p_vaddr;
				app->tls_len = phdr->p_filesz;
				app->tls_size = phdr->p_memsz;
				app->tls_align = phdr->p_align;
			}
		}
		if (app->tls_size) app->tls_image = (char *)app->base + tls_image;
		if (interp_off) lib->name = (char *)app->base + interp_off;
		if ((aux[0] & (1UL<<AT_EXECFN))
		    && strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
			app->name = (char *)aux[AT_EXECFN];
		else
			app->name = argv[0];
		kernel_mapped_dso(app);
	} else {
		int fd;
		char *ldname = argv[0];
		size_t l = strlen(ldname);
		if (l >= 3 && !strcmp(ldname+l-3, "ldd")) ldd_mode = 1;
		*argv++ = (void *)-1;
		while (argv[0] && argv[0][0]=='-' && argv[0][1]=='-') {
			char *opt = argv[0]+2;
			*argv++ = (void *)-1;
			if (!*opt) {
				break;
			} else if (!memcmp(opt, "list", 5)) {
				ldd_mode = 1;
			} else if (!memcmp(opt, "library-path", 12)) {
				if (opt[12]=='=') env_path = opt+13;
				else if (opt[12]) *argv = 0;
				else if (*argv) env_path = *argv++;
			} else if (!memcmp(opt, "preload", 7)) {
				if (opt[7]=='=') env_preload = opt+8;
				else if (opt[7]) *argv = 0;
				else if (*argv) env_preload = *argv++;
			} else {
				argv[0] = 0;
			}
			argv[-1] = (void *)-1;
		}
		if (!argv[0]) {
			dprintf(2, "musl libc\n"
				"Version %s\n"
				"Dynamic Program Loader\n"
				"Usage: %s [options] [--] pathname%s\n",
				__libc_get_version(), ldname,
				ldd_mode ? "" : " [args]");
			_exit(1);
		}
		fd = open(argv[0], O_RDONLY);
		if (fd < 0) {
			dprintf(2, "%s: cannot load %s: %s\n", ldname, argv[0], strerror(errno));
			_exit(1);
		}
		runtime = 1;
		ehdr = (void *)map_library(fd, app);
		if (!ehdr) {
			dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]);
			_exit(1);
		}
		runtime = 0;
		close(fd);
		lib->name = ldname;
		app->name = argv[0];
		aux[AT_ENTRY] = (size_t)app->base + ehdr->e_entry;
		/* Find the name that would have been used for the dynamic
		 * linker had ldd not taken its place. */
		if (ldd_mode) {
			for (i=0; i<app->phnum; i++) {
				if (app->phdr[i].p_type == PT_INTERP)
					lib->name = (void *)(app->base
						+ app->phdr[i].p_vaddr);
			}
			dprintf(1, "\t%s (%p)\n", lib->name, lib->base);
		}
	}
	if (app->tls_size) {
		app->tls_id = tls_cnt = 1;
#ifdef TLS_ABOVE_TP
		app->tls_offset = 0;
		tls_offset = app->tls_size
			+ ( -((uintptr_t)app->tls_image + app->tls_size)
			& (app->tls_align-1) );
#else
		tls_offset = app->tls_offset = app->tls_size
			+ ( -((uintptr_t)app->tls_image + app->tls_size)
			& (app->tls_align-1) );
#endif
		tls_align = MAXP2(tls_align, app->tls_align);
	}
	app->global = 1;
	decode_dyn(app);

	/* Attach to vdso, if provided by the kernel */
	if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) {
		ehdr = (void *)vdso_base;
		vdso->phdr = phdr = (void *)(vdso_base + ehdr->e_phoff);
		vdso->phnum = ehdr->e_phnum;
		vdso->phentsize = ehdr->e_phentsize;
		for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) {
			if (phdr->p_type == PT_DYNAMIC)
				vdso->dynv = (void *)(vdso_base + phdr->p_offset);
			if (phdr->p_type == PT_LOAD)
				vdso->base = (void *)(vdso_base - phdr->p_vaddr + phdr->p_offset);
		}
		vdso->name = "";
		vdso->shortname = "linux-gate.so.1";
		vdso->global = 1;
		decode_dyn(vdso);
		vdso->prev = lib;
		lib->next = vdso;
	}

	/* Initial dso chain consists only of the app. We temporarily
	 * append the dynamic linker/libc so we can relocate it, then
	 * restore the initial chain in preparation for loading third
	 * party libraries (preload/needed). */
	head = tail = app;
	ldso = lib;
	app->next = lib;
	reloc_all(lib);
	app->next = 0;

	/* PAST THIS POINT, ALL LIBC INTERFACES ARE FULLY USABLE. */

	/* Donate unused parts of app and library mapping to malloc */
	reclaim_gaps(app);
	reclaim_gaps(lib);

	/* Load preload/needed libraries, add their symbols to the global
	 * namespace, and perform all remaining relocations. The main
	 * program must be relocated LAST since it may contain copy
	 * relocations which depend on libraries' relocations. */
	if (env_preload) load_preload(env_preload);
	load_deps(app);
	make_global(app);

#ifndef DYNAMIC_IS_RO
	for (i=0; app->dynv[i]; i+=2)
		if (app->dynv[i]==DT_DEBUG)
			app->dynv[i+1] = (size_t)&debug;
#endif

	reloc_all(app->next);
	reloc_all(app);

	update_tls_size();
	if (libc.tls_size > sizeof builtin_tls) {
		initial_tls = calloc(libc.tls_size, 1);
		if (!initial_tls) {
			dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n",
				argv[0], libc.tls_size);
			_exit(127);
		}
	} else {
		initial_tls = builtin_tls;
	}
	if (__init_tp(__copy_tls(initial_tls)) < 0 && tls_cnt) {
		dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]);
		_exit(127);
	}
	static_tls_cnt = tls_cnt;

	if (ldso_fail) _exit(127);
	if (ldd_mode) _exit(0);

	/* Switch to runtime mode: any further failures in the dynamic
	 * linker are a reportable failure rather than a fatal startup
	 * error. If the dynamic loader (dlopen) will not be used, free
	 * all memory used by the dynamic linker. */
	runtime = 1;

	debug.ver = 1;
	debug.bp = _dl_debug_state;
	debug.head = head;
	debug.base = lib->base;
	debug.state = 0;
	_dl_debug_state();

	__init_libc(envp, argv[0]);
	atexit(do_fini);
	errno = 0;
	do_init_fini(tail);

	return (void *)aux[AT_ENTRY];
}

void *dlopen(const char *file, int mode)
{
	struct dso *volatile p, *orig_tail, *next;
	size_t orig_tls_cnt, orig_tls_offset, orig_tls_align;
	size_t i;
	int cs;
	jmp_buf jb;

	if (!file) return head;

	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
	pthread_rwlock_wrlock(&lock);
	__inhibit_ptc();

	p = 0;
	orig_tls_cnt = tls_cnt;
	orig_tls_offset = tls_offset;
	orig_tls_align = tls_align;
	orig_tail = tail;
	noload = mode & RTLD_NOLOAD;

	rtld_fail = &jb;
	if (setjmp(*rtld_fail)) {
		/* Clean up anything new that was (partially) loaded */
		if (p && p->deps) for (i=0; p->deps[i]; i++)
			if (p->deps[i]->global < 0)
				p->deps[i]->global = 0;
		for (p=orig_tail->next; p; p=next) {
			next = p->next;
			munmap(p->map, p->map_len);
			while (p->td_index) {
				void *tmp = p->td_index->next;
				free(p->td_index);
				p->td_index = tmp;
			}
			free(p->deps);
			free(p);
		}
		tls_cnt = orig_tls_cnt;
		tls_offset = orig_tls_offset;
		tls_align = orig_tls_align;
		tail = orig_tail;
		tail->next = 0;
		p = 0;
		errflag = 1;
		goto end;
	} else p = load_library(file, head);

	if (!p) {
		snprintf(errbuf, sizeof errbuf, noload ?
			"Library %s is not already loaded" :
			"Error loading shared library %s: %m",
			file);
		errflag = 1;
		goto end;
	}

	/* First load handling */
	if (!p->deps) {
		load_deps(p);
		if (p->deps) for (i=0; p->deps[i]; i++)
			if (!p->deps[i]->global)
				p->deps[i]->global = -1;
		if (!p->global) p->global = -1;
		reloc_all(p);
		if (p->deps) for (i=0; p->deps[i]; i++)
			if (p->deps[i]->global < 0)
				p->deps[i]->global = 0;
		if (p->global < 0) p->global = 0;
	}

	if (mode & RTLD_GLOBAL) {
		if (p->deps) for (i=0; p->deps[i]; i++)
			p->deps[i]->global = 1;
		p->global = 1;
	}

	update_tls_size();
	_dl_debug_state();
	orig_tail = tail;
end:
	__release_ptc();
	if (p) gencnt++;
	pthread_rwlock_unlock(&lock);
	if (p) do_init_fini(orig_tail);
	pthread_setcancelstate(cs, 0);
	return p;
}

static int invalid_dso_handle(void *h)
{
	struct dso *p;
	for (p=head; p; p=p->next) if (h==p) return 0;
	snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h);
	errflag = 1;
	return 1;
}

void *__tls_get_addr(size_t *);

static void *do_dlsym(struct dso *p, const char *s, void *ra)
{
	size_t i;
	uint32_t h = 0, gh = 0;
	Sym *sym;
	if (p == head || p == RTLD_DEFAULT || p == RTLD_NEXT) {
		if (p == RTLD_DEFAULT) {
			p = head;
		} else if (p == RTLD_NEXT) {
			for (p=head; p && (unsigned char *)ra-p->map>p->map_len; p=p->next);
			if (!p) p=head;
			p = p->next;
		}
		struct symdef def = find_sym(p, s, 0);
		if (!def.sym) goto failed;
		if ((def.sym->st_info&0xf) == STT_TLS)
			return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value});
		return def.dso->base + def.sym->st_value;
	}
	if (p != RTLD_DEFAULT && p != RTLD_NEXT && invalid_dso_handle(p))
		return 0;
	if (p->ghashtab) {
		gh = gnu_hash(s);
		sym = gnu_lookup(s, gh, p);
	} else {
		h = sysv_hash(s);
		sym = sysv_lookup(s, h, p);
	}
	if (sym && (sym->st_info&0xf) == STT_TLS)
		return __tls_get_addr((size_t []){p->tls_id, sym->st_value});
	if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
		return p->base + sym->st_value;
	if (p->deps) for (i=0; p->deps[i]; i++) {
		if (p->deps[i]->ghashtab) {
			if (!gh) gh = gnu_hash(s);
			sym = gnu_lookup(s, gh, p->deps[i]);
		} else {
			if (!h) h = sysv_hash(s);
			sym = sysv_lookup(s, h, p->deps[i]);
		}
		if (sym && (sym->st_info&0xf) == STT_TLS)
			return __tls_get_addr((size_t []){p->deps[i]->tls_id, sym->st_value});
		if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
			return p->deps[i]->base + sym->st_value;
	}
failed:
	errflag = 1;
	snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s);
	return 0;
}

int __dladdr(const void *addr, Dl_info *info)
{
	struct dso *p;
	Sym *sym;
	uint32_t nsym;
	char *strings;
	size_t i;
	void *best = 0;
	char *bestname;

	pthread_rwlock_rdlock(&lock);
	for (p=head; p && (unsigned char *)addr-p->map>p->map_len; p=p->next);
	pthread_rwlock_unlock(&lock);

	if (!p) return 0;

	sym = p->syms;
	strings = p->strings;
	if (p->hashtab) {
		nsym = p->hashtab[1];
	} else {
		uint32_t *buckets;
		uint32_t *hashval;
		buckets = p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4);
		sym += p->ghashtab[1];
		for (i = nsym = 0; i < p->ghashtab[0]; i++) {
			if (buckets[i] > nsym)
				nsym = buckets[i];
		}
		if (nsym) {
			nsym -= p->ghashtab[1];
			hashval = buckets + p->ghashtab[0] + nsym;
			do nsym++;
			while (!(*hashval++ & 1));
		}
	}

	for (; nsym; nsym--, sym++) {
		if (sym->st_value
		 && (1<<(sym->st_info&0xf) & OK_TYPES)
		 && (1<<(sym->st_info>>4) & OK_BINDS)) {
			void *symaddr = p->base + sym->st_value;
			if (symaddr > addr || symaddr < best)
				continue;
			best = symaddr;
			bestname = strings + sym->st_name;
			if (addr == symaddr)
				break;
		}
	}

	if (!best) return 0;

	info->dli_fname = p->name;
	info->dli_fbase = p->base;
	info->dli_sname = bestname;
	info->dli_saddr = best;

	return 1;
}

void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
{
	void *res;
	pthread_rwlock_rdlock(&lock);
	res = do_dlsym(p, s, ra);
	pthread_rwlock_unlock(&lock);
	return res;
}

int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
{
	struct dso *current;
	struct dl_phdr_info info;
	int ret = 0;
	for(current = head; current;) {
		info.dlpi_addr      = (uintptr_t)current->base;
		info.dlpi_name      = current->name;
		info.dlpi_phdr      = current->phdr;
		info.dlpi_phnum     = current->phnum;
		info.dlpi_adds      = gencnt;
		info.dlpi_subs      = 0;
		info.dlpi_tls_modid = current->tls_id;
		info.dlpi_tls_data  = current->tls_image;

		ret = (callback)(&info, sizeof (info), data);

		if (ret != 0) break;

		pthread_rwlock_rdlock(&lock);
		current = current->next;
		pthread_rwlock_unlock(&lock);
	}
	return ret;
}
#else
static int invalid_dso_handle(void *h)
{
	snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h);
	errflag = 1;
	return 1;
}
void *dlopen(const char *file, int mode)
{
	strcpy(errbuf, "Dynamic loading not supported");
	errflag = 1;
	return 0;
}
void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
{
	errflag = 1;
	snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s);
	return 0;
}
int __dladdr (const void *addr, Dl_info *info)
{
	return 0;
}
#endif

int __dlinfo(void *dso, int req, void *res)
{
	if (invalid_dso_handle(dso)) return -1;
	if (req != RTLD_DI_LINKMAP) {
		snprintf(errbuf, sizeof errbuf, "Unsupported request %d", req);
		errflag = 1;
		return -1;
	}
	*(struct link_map **)res = dso;
	return 0;
}

char *dlerror()
{
	if (!errflag) return 0;
	errflag = 0;
	return errbuf;
}

int dlclose(void *p)
{
	return invalid_dso_handle(p);
}


text:
#include <stdlib.h>

/* stub for archs that lack dynamic linker support */

void _dlstart()
{
	_Exit(1);
}


text:
#include <dlfcn.h>

void *__dlsym(void *restrict, const char *restrict, void *restrict);

void *dlsym(void *restrict p, const char *restrict s)
{
	return __dlsym(p, s, 0);
}


text:
#ifdef SHARED

#include <stddef.h>
#include "libc.h"

ptrdiff_t __tlsdesc_static()
{
	return 0;
}

weak_alias(__tlsdesc_static, __tlsdesc_dynamic);

#endif


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

int __dlinfo(void *, int, void *);

int dlinfo(void *dso, int req, void *res)
{
	return __dlinfo(dso, req, res);
}


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

int __dladdr(const void *, Dl_info *);

int dladdr(const void *addr, Dl_info *info)
{
	return __dladdr(addr, info);
}


text:
#ifndef SHARED

#include <elf.h>
#include <link.h>
#include "libc.h"

#define AUX_CNT 38

int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
{
	unsigned char *p;
	ElfW(Phdr) *phdr, *tls_phdr=0;
	size_t base = 0;
	size_t n;
	struct dl_phdr_info info;
	size_t i, aux[AUX_CNT];

	for (i=0; libc.auxv[i]; i+=2)
		if (libc.auxv[i]<AUX_CNT) aux[libc.auxv[i]] = libc.auxv[i+1];

	for (p=(void *)aux[AT_PHDR],n=aux[AT_PHNUM]; n; n--,p+=aux[AT_PHENT]) {
		phdr = (void *)p;
		if (phdr->p_type == PT_PHDR)
			base = aux[AT_PHDR] - phdr->p_vaddr;
		if (phdr->p_type == PT_TLS)
			tls_phdr = phdr;
	}
	info.dlpi_addr  = base;
	info.dlpi_name  = "/proc/self/exe";
	info.dlpi_phdr  = (void *)aux[AT_PHDR];
	info.dlpi_phnum = aux[AT_PHNUM];
	info.dlpi_adds  = 0;
	info.dlpi_subs  = 0;
	if (tls_phdr) {
		info.dlpi_tls_modid = 1;
		info.dlpi_tls_data = (void *)(base + tls_phdr->p_vaddr);
	} else {
		info.dlpi_tls_modid = 0;
		info.dlpi_tls_data = 0;
	}
	return (callback)(&info, sizeof (info), data);
}
#endif


text:
	.global _dlstart
	.type   _dlstart,@function
_dlstart:
	bl      1f
2:	.long   _DYNAMIC-2b
1:	mflr    5
	lwz     0, 0(5)
	add     5, 0, 5
	lwz     3, 0(1)
	addi    4, 1, 4
	addi    1, 1, -16
	bl      __reloc_self

	lwz     3, 16(1)
	addi    4, 1, 20
	bl      __dynlink
	addi    1, 1, 16

	lwz     4, 0(1)
1:	addi    4, 4, -1
	lwzu    5, 4(1)
	cmpwi   5, -1
	beq-    1b
	addi    4, 4, 1
	stwu    4, -4(1)

	mtlr    3
	li      3, 0
	blr


text:
	.text
	.global dlsym
	.type   dlsym,@function
dlsym:
	mflr    5                      # The return address is arg3.
	b       __dlsym
	.end    dlsym
	.size   dlsym, .-dlsym


text:
.text
.global _dlstart
.type   _dlstart, @function
_dlstart:
	mov.l  @r15, r4
	mov    r15, r5
	mov.l  L1, r0
	bsrf   r0
	 add   #4, r5

2:	mov    r0, r2
	mov.l  @r15+, r1
1:	mov.l  @r15+, r0
	cmp/eq #-1, r0
	bt/s   1b
	 add   #-1, r1

	add    #1, r1
	mov.l  r0, @-r15
	mov.l  r1, @-r15
	mov    #0, r4
	jmp    @r2
	 nop

.align 2
L1:	.long __dynlink@PLT-(2b-.)


text:
.text
.global dlsym
.type   dlsym, @function
dlsym:
	mov.l L1, r0
	braf  r0
1:	 mov.l @r15, r6

.align 2
L1:	.long __dlsym@PLT-(1b-.)


text:
# FIXME: clearing argv entries
.global _dlstart
_dlstart:
	add     r19, r0, r0

	lw      r5, r0, r1
	addi    r6, r1, 4
	mfs     r7, rpc
	addi    r7, r7, _GLOBAL_OFFSET_TABLE_+8
	addi    r7, r7, _DYNAMIC@GOTOFF
	brlid   r15, __reloc_self@PLT
	addik   r1, r1, -16

	lwi     r5, r1, 16
	brlid   r15, __dynlink@PLT
	addi    r6, r1, 20
	addik   r1, r1, 16

	lwi     r4, r1, 0
1:	lwi     r5, r1, 4
	addi    r5, r5, 1
	bnei    r5, 1f
	addi    r4, r4, -1
	addi    r1, r1, 4
	bri     1b
1:	swi     r4, r1, 0
	add     r5, r0, r0
	bra     r3


text:
.global dlsym
.type   dlsym,@function
dlsym:
	brid    __dlsym@PLT
	add     r7, r15, r0


text:
.hidden _DYNAMIC
.hidden __reloc_self
.set noreorder
.set nomacro
.global _dlstart
.type _dlstart,@function
_dlstart:
	move $fp, $0

	bgezal $0, 1f
	nop
2:	.gpword 2b
	.gpword _DYNAMIC
	.gpword __reloc_self
1:	lw $gp, 0($ra)
	subu $gp, $ra, $gp

	lw $4, 0($sp)
	addiu $5, $sp, 4
	lw $6, 4($ra)
	addu $6, $6, $gp
	addiu $7, $gp, -0x7ff0
	subu $sp, $sp, 16
	lw $25, 8($ra)
	add $25, $25, $gp
	jalr $25
	nop

	lw $25, %call16(__dynlink)($gp)
	lw $4, 16($sp)
	addiu $5, $sp, 20
	jalr $25
	nop

	add $sp, $sp, 16
	li $6, -1
	lw $4, ($sp)
1:	lw $5, 4($sp)
	bne $5, $6, 2f
	nop
	addu $sp, $sp, 4
	addu $4, $4, -1
	b 1b
	nop
2:	sw $4, ($sp)
	jr $2


text:
.set noreorder
.global dlsym
.type dlsym,@function
dlsym:
	lui $gp, %hi(_gp_disp)
	addiu $gp, %lo(_gp_disp)
	addu $gp, $gp, $25
	move $6, $ra
	lw $25, %call16(__dlsym)($gp)
	addiu $sp, $sp, -16
	sw $ra, 12($sp)
	jalr $25
	nop
	lw $ra, 12($sp)
	jr $ra
	addiu $sp, $sp, 16


text:
.text
.global __tlsdesc_static
.type __tlsdesc_static,@function
__tlsdesc_static:
	mov 4(%eax),%eax
	ret

.global __tlsdesc_dynamic
.type __tlsdesc_dynamic,@function
__tlsdesc_dynamic:
	mov 4(%eax),%eax
	push %edx
	mov %gs:4,%edx
	push %ecx
	mov (%eax),%ecx
	cmp %ecx,(%edx)
	jc 1f
	mov 4(%eax),%eax
	add (%edx,%ecx,4),%eax
2:	pop %ecx
	sub %gs:0,%eax
	pop %edx
	ret
1:	push %eax
	call __tls_get_addr
	pop %ecx
	jmp 2b


text:
.text
.global _dlstart
_dlstart:
	xor %ebp,%ebp
	pop %edi
	mov %esp,%esi
	and $-16,%esp
	push %ebp
	push %ebp
	push %esi
	push %edi
	call __dynlink
	mov %esi,%esp
1:	dec %edi
	pop %esi
	cmp $-1,%esi
	jz 1b
	inc %edi
	push %esi
	push %edi
	xor %edx,%edx
	jmp *%eax


text:
.text
.global dlsym
.type dlsym,@function
dlsym:
	push (%esp)
	push 12(%esp)
	push 12(%esp)
	call __dlsym
	add $12,%esp
	ret


text:
.text
.global _dlstart
_dlstart:
	mov (%rsp),%rdi  /* move argc into 1st argument slot */
	lea 4(%rsp),%rsi /* move argv into 2nd argument slot */
	call __dynlink
	/* in case the dynlinker was called directly, it sets the "consumed"
	   argv values to -1. so we must loop over the array as long as -1
	   is in the top argv slot, decrement argc, and then set the stackpointer
	   to the new argc as well as argc's new value.
	   as the x32 abi has longs in the argv array, we cannot use push/pop.*/
	movl (%rsp),%edi /* copy argc into edi */
	xor %rdx,%rdx /* we use rdx as an offset to the current argv member */
1:	dec %edi
	addl $4, %edx
	movl (%rsp, %rdx), %esi
	cmp $-1,%esi
	jz 1b
	inc %edi
	subl $4, %edx
	lea (%rsp, %rdx), %rsp /* set rsp to new argv[-1] */
	movl %edi, (%rsp)      /* write new argc there */
	xor %edx,%edx
	jmp *%rax


text:
.text
.global dlsym
.type dlsym,@function
dlsym:
	mov (%rsp),%rdx
	jmp __dlsym


text:
.text
.global _dlstart
_dlstart:
	ldr r0,[sp]
	add r1,sp,#4
	bl __dynlink
	pop {r1}
1:	sub r1,r1,#1
	pop {r2}
	cmp r2,#-1
	beq 1b
	add r1,r1,#1
	push {r1,r2}
	mov r1,r0
	mov r0,#0
	tst r1,#1
	moveq pc,r1
	bx r1


text:
.text
.global dlsym
.type dlsym,%function
dlsym:
	mov r2,lr
	b __dlsym


text:
.global _dlstart
_dlstart:
	l.jal	1f
	 l.nop
1:	l.movhi	r5, gotpchi(_GLOBAL_OFFSET_TABLE_+0)
	l.ori	r5, r5, gotpclo(_GLOBAL_OFFSET_TABLE_+4)
	l.add	r5, r5, r9
	l.movhi	r3, gotoffhi(_DYNAMIC)
	l.ori	r3, r3, gotofflo(_DYNAMIC)
	l.add	r5, r5, r3

	l.lwz	r3, 0(r1)
	l.addi	r4, r1, 4
	l.jal	plt(__reloc_self)
	 l.addi	r1, r1, -16

	l.lwz	r3, 16(r1)
	l.jal	plt(__dynlink)
	 l.addi	r4, r1, 20
	l.addi	r1, r1, 16

	l.lwz	r4, 0(r1)
1:	l.addi	r4, r4, -1
	l.lwz	r5, 4(r1)
	l.sfeqi	r5, -1
	l.bf	1b
	 l.addi	r1, r1, 4

	l.addi	r4, r4, 1
	l.addi	r1, r1, -4
	l.sw	0(r1), r4

	l.jr	r11
	 l.ori	r3, r0, 0


text:
.global dlsym
.type   dlsym,@function
dlsym:
	l.j	plt(__dlsym)
	 l.ori	r5, r9, 0


text:
.text
.global __tlsdesc_static
.type __tlsdesc_static,@function
__tlsdesc_static:
	mov 8(%rax),%rax
	ret

.global __tlsdesc_dynamic
.type __tlsdesc_dynamic,@function
__tlsdesc_dynamic:
	mov 8(%rax),%rax
	push %rdx
	mov %fs:8,%rdx
	push %rcx
	mov (%rax),%rcx
	cmp %rcx,(%rdx)
	jc 1f
	mov 8(%rax),%rax
	add (%rdx,%rcx,8),%rax
2:	pop %rcx
	sub %fs:0,%rax
	pop %rdx
	ret
1:	push %rdi
	push %rdi
	push %rsi
	push %r8
	push %r9
	push %r10
	push %r11
	mov %rax,%rdi
	call __tls_get_addr
	pop %r11
	pop %r10
	pop %r9
	pop %r8
	pop %rsi
	pop %rdi
	pop %rdi
	jmp 2b


text:
.text
.global _dlstart
_dlstart:
	mov (%rsp),%rdi
	lea 8(%rsp),%rsi
	call __dynlink
	pop %rdi
1:	dec %edi
	pop %rsi
	cmp $-1,%rsi
	jz 1b
	inc %edi
	push %rsi
	push %rdi
	xor %edx,%edx
	jmp *%rax