OpenSolaris_b135/lib/brand/lx/lx_brand/common/lx_brand.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <sys/inttypes.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/fstyp.h>
#include <sys/fsid.h>
#include <sys/systm.h>
#include <sys/auxv.h>
#include <sys/frame.h>
#include <sys/brand.h>

#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#include <synch.h>
#include <libelf.h>
#include <libgen.h>
#include <pthread.h>
#include <utime.h>
#include <dirent.h>
#include <ucontext.h>
#include <libintl.h>
#include <locale.h>

#include <sys/lx_misc.h>
#include <sys/lx_debug.h>
#include <sys/lx_brand.h>
#include <sys/lx_types.h>
#include <sys/lx_stat.h>
#include <sys/lx_statfs.h>
#include <sys/lx_ioctl.h>
#include <sys/lx_signal.h>
#include <sys/lx_syscall.h>
#include <sys/lx_thread.h>
#include <sys/lx_thunk_server.h>

/*
 * Map solaris errno to the linux equivalent.
 */
static int stol_errno[] = {
	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,  42,  43,  44,  45,  46,
	47,  48,  49,  50,  51,  35,  47,  22,  38,  22, /* 49 */
	52,  53,  54,  55,  56,  57,  58,  59,  22,  22,
	61,  61,  62,  63,  64,  65,  66,  67,  68,  69,
	70,  71,  22,  22,  72,  22,  22,  74,  36,  75,
	76,  77,  78,  79,  80,  81,  82,  83,  84,  38,
	40,  85,  86,  39,  87,  88,  89,  90,  91,  92, /* 99 */
	22,  22,  22,  22,  22,  22,  22,  22,  22,  22,
	22,  22,  22,  22,  22,  22,  22,  22,  22,  22,
	93,  94,  95,  96,  97,  98,  99, 100, 101, 102,
	103, 104, 105, 106, 107,  22,  22,  22,  22,  22,
	22,  22,  22, 108, 109, 110, 111, 112, 113, 114, /* 149 */
	115, 116
};

char lx_release[128];

/*
 * Map a linux locale ending string to the solaris equivalent.
 */
struct lx_locale_ending {
	const char	*linux_end;	/* linux ending string */
	const char	*solaris_end;	/* to transform with this string */
	int		le_size;	/* linux ending string length */
	int		se_size;	/* solaris ending string length */
};

#define	l2s_locale(lname, sname) \
	{(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1}

static struct lx_locale_ending lx_locales[] = {
	l2s_locale(".utf8",	 ".UTF-8"),
	l2s_locale(".utf8@euro", ".UTF-8"),
	l2s_locale("@euro",	 ".ISO8859-15"),
	l2s_locale(".iso885915", ".ISO8859-15"),
	l2s_locale(".euckr",	 ".EUC"),
	l2s_locale(".euctw",	 ".EUC"),
	l2s_locale(".koi8r",	 ".KOI8-R"),
	l2s_locale(".gb18030",	 ".GB18030"),
	l2s_locale(".gbk",	 ".GBK"),
	l2s_locale("@cyrillic",	 ".ISO8859-5")
};

#define	MAXLOCALENAMELEN	30
#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
#endif

/*
 * This flag is part of the registration with the in-kernel brand module. It's
 * used in lx_handler() to determine if we should go back into the kernel after
 * a system call in case the kernel needs to perform some post-syscall work
 * like tracing for example.
 */
int lx_traceflag;

#define	NOSYS_NULL		1
#define	NOSYS_NO_EQUIV		2
#define	NOSYS_KERNEL		3
#define	NOSYS_UNDOC		4
#define	NOSYS_OBSOLETE		5

/*
 * SYS_PASSTHRU denotes a system call we can just call on behalf of the
 * branded process without having to translate the arguments.
 *
 * The restriction on this is that the call in question MUST return -1 to
 * denote an error.
 */
#define	SYS_PASSTHRU		5

static char *nosys_msgs[] = {
	"Either not yet done, or we haven't come up with an excuse",
	"No such Linux system call",
	"No equivalent Solaris functionality",
	"Reads/modifies Linux kernel state",
	"Undocumented and/or rarely used system call",
	"Unsupported, obsolete system call"
};

struct lx_sysent {
	char    *sy_name;
	int	(*sy_callc)();
	char	sy_flags;
	char	sy_narg;
};

static struct lx_sysent sysents[LX_NSYSCALLS + 1];

static uintptr_t stack_bottom;

int lx_install = 0;		/* install mode enabled if non-zero */
boolean_t lx_is_rpm = B_FALSE;
int lx_rpm_delay = 1;
int lx_strict = 0;		/* "strict" mode enabled if non-zero */
int lx_verbose = 0;		/* verbose mode enabled if non-zero */
int lx_debug_enabled = 0;	/* debugging output enabled if non-zero */

pid_t zoneinit_pid;		/* zone init PID */

thread_key_t lx_tsd_key;

int
uucopy_unsafe(const void *src, void *dst, size_t n)
{
	bcopy(src, dst, n);
	return (0);
}

int
uucopystr_unsafe(const void *src, void *dst, size_t n)
{
	(void) strncpy((char *)src, dst, n);
	return (0);
}

static void
i_lx_msg(int fd, char *msg, va_list ap)
{
	int	i;
	char	buf[LX_MSG_MAXLEN];

	/* LINTED [possible expansion issues] */
	i = vsnprintf(buf, sizeof (buf), msg, ap);
	buf[LX_MSG_MAXLEN - 1] = '\0';
	if (i == -1)
		return;

	/* if debugging is enabled, send this message to debug output */
	if (lx_debug_enabled != 0)
		lx_debug(buf);

	/*
	 * If we are trying to print to stderr, we also want to send the
	 * message to syslog.
	 */
	if (fd == 2) {
		syslog(LOG_ERR, "%s", buf);

		/*
		 * We let the user choose whether or not to see these
		 * messages on the console.
		 */
		if (lx_verbose == 0)
			return;
	}

	/* we retry in case of EINTR */
	do {
		i = write(fd, buf, strlen(buf));
	} while ((i == -1) && (errno == EINTR));
}

/*PRINTFLIKE1*/
void
lx_err(char *msg, ...)
{
	va_list	ap;

	assert(msg != NULL);

	va_start(ap, msg);
	i_lx_msg(STDERR_FILENO, msg, ap);
	va_end(ap);
}

/*
 * This is just a non-zero exit value which also isn't one that would allow
 * us to easily detect if a branded process exited because of a recursive
 * fatal error.
 */
#define	LX_ERR_FATAL	42

/*
 * Our own custom version of abort(), this routine will be used in place
 * of the one located in libc.  The primary difference is that this version
 * will first reset the signal handler for SIGABRT to SIG_DFL, ensuring the
 * SIGABRT sent causes us to dump core and is not caught by a user program.
 */
void
abort(void)
{
	static int aborting = 0;

	struct sigaction sa;
	sigset_t sigmask;

	/* watch out for recursive calls to this function */
	if (aborting != 0)
		exit(LX_ERR_FATAL);

	aborting = 1;

	/*
	 * Block all signals here to avoid taking any signals while exiting
	 * in an effort to avoid any strange user interaction with our death.
	 */
	(void) sigfillset(&sigmask);
	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);

	/*
	 * Our own version of abort(3C) that we know will never call
	 * a user-installed SIGABRT handler first.  We WANT to die.
	 *
	 * Do this by resetting the handler to SIG_DFL, and releasing any
	 * held SIGABRTs.
	 *
	 * If no SIGABRTs are pending, send ourselves one.
	 *
	 * The while loop is a bit of overkill, but abort(3C) does it to
	 * assure it never returns so we will as well.
	 */
	(void) sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = SIG_DFL;
	sa.sa_flags = 0;

	for (;;) {
		(void) sigaction(SIGABRT, &sa, NULL);
		(void) sigrelse(SIGABRT);
		(void) thr_kill(thr_self(), SIGABRT);
	}

	/*NOTREACHED*/
}

/*PRINTFLIKE1*/
void
lx_msg(char *msg, ...)
{
	va_list	ap;

	assert(msg != NULL);
	va_start(ap, msg);
	i_lx_msg(STDOUT_FILENO, msg, ap);
	va_end(ap);
}

/*PRINTFLIKE1*/
void
lx_err_fatal(char *msg, ...)
{
	va_list	ap;

	assert(msg != NULL);

	va_start(ap, msg);
	i_lx_msg(STDERR_FILENO, msg, ap);
	va_end(ap);
	abort();
}

/*
 * See if it is safe to alloca() sz bytes.  Return 1 for yes, 0 for no.
 */
int
lx_check_alloca(size_t sz)
{
	uintptr_t sp = (uintptr_t)&sz;
	uintptr_t end = sp - sz;

	return ((end < sp) && (end >= stack_bottom));
}

/*PRINTFLIKE1*/
void
lx_unsupported(char *msg, ...)
{
	va_list	ap;

	assert(msg != NULL);

	/* send the msg to the error stream */
	va_start(ap, msg);
	i_lx_msg(STDERR_FILENO, msg, ap);
	va_end(ap);

	/*
	 * If the user doesn't trust the application to responsibly
	 * handle ENOTSUP, we kill the application.
	 */
	if (lx_strict)
		(void) kill(getpid(), SIGSYS);
}

extern void lx_runexe(void *argv, int32_t entry);
int lx_init(int argc, char *argv[], char *envp[]);

static int
lx_emulate_args(lx_regs_t *rp, struct lx_sysent *s, uintptr_t *args)
{
	/*
	 * If the system call takes 6 args, then libc has stashed them in
	 * memory at the address contained in %ebx. Except for some syscalls
	 * which store the 6th argument in %ebp.
	 */
	if (s->sy_narg == 6 && !(s->sy_flags & EBP_HAS_ARG6)) {
		if (uucopy((void *)rp->lxr_ebx, args,
		    sizeof (args[0]) * 6) != 0)
			return (-stol_errno[errno]);
	} else {
		args[0] = rp->lxr_ebx;
		args[1] = rp->lxr_ecx;
		args[2] = rp->lxr_edx;
		args[3] = rp->lxr_esi;
		args[4] = rp->lxr_edi;
		args[5] = rp->lxr_ebp;
	}

	return (0);
}

void
lx_emulate(lx_regs_t *rp)
{
	struct lx_sysent *s;
	uintptr_t args[6];
	uintptr_t gs = rp->lxr_gs & 0xffff;	/* %gs is only 16 bits */
	int syscall_num, ret;

	syscall_num = rp->lxr_eax;

	/*
	 * lx_brand_int80_callback() ensures that the syscall_num is sane;
	 * Use it as is.
	 */
	assert(syscall_num >= 0);
	assert(syscall_num < (sizeof (sysents) / sizeof (sysents[0])));
	s = &sysents[syscall_num];

	if ((ret = lx_emulate_args(rp, s, args)) != 0)
		goto out;

	/*
	 * If the tracing flag is enabled we call into the brand-specific
	 * kernel module to handle the tracing activity (DTrace or ptrace).
	 * It would be tempting to perform DTrace activity in the brand
	 * module's syscall trap callback, rather than having to return
	 * to the kernel here, but -- since argument encoding can vary
	 * according to the specific system call -- that would require
	 * replicating the knowledge of argument decoding in the kernel
	 * module as well as here in the brand library.
	 */
	if (lx_traceflag != 0) {
		/*
		 * Part of the ptrace "interface" is that on syscall entry
		 * %eax should be reported as -ENOSYS while the orig_eax
		 * field of the user structure needs to contain the actual
		 * system call number. If we end up stopping here, the
		 * controlling process will dig the lx_regs_t structure out of
		 * our stack.
		 */
		rp->lxr_orig_eax = syscall_num;
		rp->lxr_eax = -stol_errno[ENOSYS];

		(void) syscall(SYS_brand, B_SYSENTRY, syscall_num, args);

		/*
		 * The external tracer may have modified the arguments to this
		 * system call. Refresh the argument cache to account for this.
		 */
		if ((ret = lx_emulate_args(rp, s, args)) != 0)
			goto out;
	}

	if (s->sy_callc == NULL) {
		lx_unsupported(gettext("unimplemented syscall #%d (%s): %s\n"),
		    syscall_num, s->sy_name, nosys_msgs[s->sy_flags]);
		ret = -stol_errno[ENOTSUP];
		goto out;
	}

	if (lx_debug_enabled != 0) {
		const char *fmt;

		switch (s->sy_narg) {
		case 0:
			fmt = "calling %s()";
			break;
		case 1:
			fmt = "calling %s(0x%p)";
			break;
		case 2:
			fmt = "calling %s(0x%p, 0x%p)";
			break;
		case 3:
			fmt = "calling %s(0x%p, 0x%p, 0x%p)";
			break;
		case 4:
			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p)";
			break;
		case 5:
			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p, 0x%p)";
			break;
		case 6:
			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p, 0x%p, 0x%p)";
			break;
		}

		lx_debug(fmt, s->sy_name, args[0], args[1], args[2], args[3],
		    args[4], args[5]);
	}

	if (gs != LWPGS_SEL) {
		lx_tsd_t *lx_tsd;

		/*
		 * While a %gs of 0 is technically legal (as long as the
		 * application never dereferences memory using %gs), Solaris
		 * has its own ideas as to how a zero %gs should be handled in
		 * _update_sregs(), such that any 32-bit user process with a
		 * %gs of zero running on a system with a 64-bit kernel will
		 * have its %gs hidden base register stomped on on return from
		 * a system call, leaving an incorrect base address in place
		 * until the next time %gs is actually reloaded (forcing a
		 * reload of the base address from the appropriate descriptor
		 * table.)
		 *
		 * Of course the kernel will once again stomp on THAT base
		 * address when returning from a system call, resulting in an
		 * an application segmentation fault.
		 *
		 * To avoid this situation, disallow a save of a zero %gs
		 * here in order to try and capture any Linux process that
		 * attempts to make a syscall with a zero %gs installed.
		 */
		assert(gs != 0);

		if ((ret = thr_getspecific(lx_tsd_key,
		    (void **)&lx_tsd)) != 0)
			lx_err_fatal(gettext(
			    "%s: unable to read thread-specific data: %s"),
			    "lx_emulate", strerror(ret));

		assert(lx_tsd != 0);

		lx_tsd->lxtsd_gs = gs;

		lx_debug("lx_emulate(): gsp 0x%p, saved gs: 0x%x", lx_tsd, gs);
	}

	if (s->sy_flags == SYS_PASSTHRU)
		lx_debug("\tCalling Solaris %s()", s->sy_name);

	ret = s->sy_callc(args[0], args[1], args[2], args[3], args[4], args[5]);

	if (ret > -65536 && ret < 65536)
		lx_debug("\t= %d", ret);
	else
		lx_debug("\t= 0x%x", ret);

	if ((s->sy_flags == SYS_PASSTHRU) && (ret == -1)) {
		ret = -stol_errno[errno];
	} else {
		/*
		 * If the return value is between -4096 and 0 we assume it's an
		 * error, so we translate the Solaris error number into the
		 * Linux equivalent.
		 */
		if (ret < 0 && ret > -4096) {
			if (-ret >=
			    sizeof (stol_errno) / sizeof (stol_errno[0])) {
				lx_debug("Invalid return value from emulated "
				    "syscall %d (%s): %d\n",
				    syscall_num, s->sy_name, ret);
				assert(0);
			}

			ret = -stol_errno[-ret];
		}
	}

out:
	/*
	 * %eax holds the return code from the system call.
	 */
	rp->lxr_eax = ret;

	/*
	 * If the trace flag is set, bounce into the kernel to let it do
	 * any necessary tracing (DTrace or ptrace).
	 */
	if (lx_traceflag != 0) {
		rp->lxr_orig_eax = syscall_num;
		(void) syscall(SYS_brand, B_SYSRETURN, syscall_num, ret);
	}
}

/* Transform the Linux locale name to make it look like a Solaris locale name */
static const char *
lx_translate_locale(char *translated_name_mem, int mem_size)
{
	char *loc;
	int i;
	size_t len;

	if ((loc = getenv("LC_ALL")) == NULL)
		if ((loc = getenv("LANG")) == NULL)
			return ("C");

	if (strlcpy(translated_name_mem, loc, mem_size) >= mem_size)
		return ("");

	len = strlen(loc);

	/* replace the end of the locale name if it's a known pattern */
	for (i = 0; i < sizeof (lx_locales) / sizeof (struct lx_locale_ending);
	    i++) {
		if (len <= lx_locales[i].le_size)
			continue;

		if (strncmp(loc + len - lx_locales[i].le_size,
		    lx_locales[i].linux_end, lx_locales[i].le_size))
			continue; /* don't match */

		if (len - lx_locales[i].le_size + lx_locales[i].se_size
		    >= mem_size)
			return ("C"); /* size too small for the new name */

		(void) strlcpy(translated_name_mem + len -
		    lx_locales[i].le_size, lx_locales[i].solaris_end,
		    lx_locales[i].se_size + 1);

		return ((const char *)translated_name_mem);
	}

	/* no match */
	return ("");
}

static void
lx_close_fh(FILE *file)
{
	int fd, fd_new;

	if (file == NULL)
		return;

	if ((fd = fileno(file)) < 0)
		return;

	fd_new = dup(fd);
	if (fd_new == -1)
		return;

	(void) fclose(file);
	(void) dup2(fd_new, fd);
	(void) close(fd_new);
}

extern int set_l10n_alternate_root(char *path);

/*ARGSUSED*/
int
lx_init(int argc, char *argv[], char *envp[])
{
	char		*r;
	auxv_t		*ap;
	int		*p, err;
	lx_elf_data_t	edp;
	lx_brand_registration_t reg;
	char 		locale_translated_name[MAXLOCALENAMELEN];
	static lx_tsd_t lx_tsd;

	/* Look up the PID that serves as init for this zone */
	if ((err = lx_lpid_to_spid(1, &zoneinit_pid)) < 0)
		lx_err_fatal(gettext(
		    "Unable to find PID for zone init process: %s"),
		    strerror(err));

	/*
	 * Ubuntu init will fail if its TERM environment variable is not set
	 * so if we are running init, and TERM is not set, we set term and
	 * reexec so that the new environment variable is propagated to the
	 * linux application stack.
	 */
	if ((getpid() == zoneinit_pid) && (getenv("TERM") == NULL)) {
		if (setenv("TERM", "vt100", 1) < 0 || execv(argv[0], argv) < 0)
			lx_err_fatal(gettext("failed to set TERM"));
	}

	if ((set_l10n_alternate_root("/native") == 0) &&
	    (setlocale(LC_ALL, lx_translate_locale(locale_translated_name,
	    sizeof (locale_translated_name))) != NULL) &&
	    (bindtextdomain(TEXT_DOMAIN, "/native/usr/lib/locale") != NULL)) {
		(void) textdomain(TEXT_DOMAIN);
	}

	stack_bottom = 2 * sysconf(_SC_PAGESIZE);

	/*
	 * We need to shutdown all libc stdio.  libc stdio normally goes to
	 * file descriptors, but since we're actually part of a linux
	 * process we don't own these file descriptors and we can't make
	 * any assumptions about their state.
	 */
	lx_close_fh(stdin);
	lx_close_fh(stdout);
	lx_close_fh(stderr);

	lx_debug_init();

	r = getenv("LX_RELEASE");
	if (r == NULL) {
		if (lx_get_kern_version() == LX_KERN_2_6)
			(void) strlcpy(lx_release, LX_UNAME_RELEASE_2_6,
			    sizeof (lx_release));
		else
			(void) strlcpy(lx_release, LX_UNAME_RELEASE_2_4,
			    sizeof (lx_release));
	} else {
		(void) strlcpy(lx_release, r, 128);
	}

	lx_debug("lx_release: %s\n", lx_release);

	/*
	 * Should we kill an application that attempts an unimplemented
	 * system call?
	 */
	if (getenv("LX_STRICT") != NULL) {
		lx_strict = 1;
		lx_debug("STRICT mode enabled.\n");
	}

	/*
	 * Are we in install mode?
	 */
	if (getenv("LX_INSTALL") != NULL) {
		lx_install = 1;
		lx_debug("INSTALL mode enabled.\n");
	}

	/*
	 * Should we attempt to send messages to the screen?
	 */
	if (getenv("LX_VERBOSE") != NULL) {
		lx_verbose = 1;
		lx_debug("VERBOSE mode enabled.\n");
	}

	lx_debug("executing linux process: %s", argv[0]);
	lx_debug("branding myself and setting handler to 0x%p",
	    (void *)lx_handler_table);

	/*
	 * The version of rpm that ships with CentOS/RHEL 3.x has a race
	 * condition in it.  If it creates a child process to run a
	 * post-install script, and that child process completes too
	 * quickly, it will disappear before the parent notices.  This
	 * causes the parent to hang forever waiting for the already dead
	 * child to die.  I'm sure there's a Lazarus joke buried in here
	 * somewhere.
	 *
	 * Anyway, as a workaround, we make every child of an 'rpm' process
	 * sleep for 1 second, giving the parent a chance to enter its
	 * wait-for-the-child-to-die loop.  Thay may be the hackiest trick
	 * in all of our Linux emulation code - and that's saying
	 * something.
	 */
	if (strcmp("rpm", basename(argv[0])) == NULL)
		lx_is_rpm = B_TRUE;

	reg.lxbr_version = LX_VERSION;
	reg.lxbr_handler = (void *)&lx_handler_table;
	reg.lxbr_tracehandler = (void *)&lx_handler_trace_table;
	reg.lxbr_traceflag = &lx_traceflag;

	/*
	 * Register the address of the user-space handler with the lx
	 * brand module.
	 */
	if (syscall(SYS_brand, B_REGISTER, &reg))
		lx_err_fatal(gettext("failed to brand the process"));

	/*
	 * Download data about the lx executable from the kernel.
	 */
	if (syscall(SYS_brand, B_ELFDATA, (void *)&edp))
		lx_err_fatal(gettext(
		    "failed to get required ELF data from the kernel"));

	if (lx_ioctl_init() != 0)
		lx_err_fatal(gettext("failed to setup the %s translator"),
		    "ioctl");

	if (lx_stat_init() != 0)
		lx_err_fatal(gettext("failed to setup the %s translator"),
		    "stat");

	if (lx_statfs_init() != 0)
		lx_err_fatal(gettext("failed to setup the %s translator"),
		    "statfs");

	/*
	 * Find the aux vector on the stack.
	 */
	p = (int *)envp;
	while (*p != NULL)
		p++;
	/*
	 * p is now pointing at the 0 word after the environ pointers. After
	 * that is the aux vectors.
	 */
	p++;
	for (ap = (auxv_t *)p; ap->a_type != 0; ap++) {
		switch (ap->a_type) {
			case AT_BASE:
				ap->a_un.a_val = edp.ed_base;
				break;
			case AT_ENTRY:
				ap->a_un.a_val = edp.ed_entry;
				break;
			case AT_PHDR:
				ap->a_un.a_val = edp.ed_phdr;
				break;
			case AT_PHENT:
				ap->a_un.a_val = edp.ed_phent;
				break;
			case AT_PHNUM:
				ap->a_un.a_val = edp.ed_phnum;
				break;
			default:
				break;
		}
	}

	/* Do any thunk server initalization. */
	lxt_server_init(argc, argv);

	/* Setup signal handler information. */
	if (lx_siginit())
		lx_err_fatal(gettext(
		    "failed to initialize lx signals for the branded process"));

	/* Setup thread-specific data area for managing linux threads. */
	if ((err = thr_keycreate(&lx_tsd_key, NULL)) != 0)
		lx_err_fatal(
		    gettext("%s failed: %s"), "thr_keycreate(lx_tsd_key)",
		    strerror(err));

	lx_debug("thr_keycreate created lx_tsd_key (%d)", lx_tsd_key);

	/* Initialize the thread specific data for this thread. */
	bzero(&lx_tsd, sizeof (lx_tsd));
	lx_tsd.lxtsd_gs = LWPGS_SEL;

	if ((err = thr_setspecific(lx_tsd_key, &lx_tsd)) != 0)
		lx_err_fatal(gettext(
		    "Unable to initialize thread-specific data: %s"),
		    strerror(err));

	/*
	 * Save the current context of this thread.
	 * We'll restore this context when this thread attempts to exit.
	 */
	if (getcontext(&lx_tsd.lxtsd_exit_context) != 0)
		lx_err_fatal(gettext(
		    "Unable to initialize thread-specific exit context: %s"),
		    strerror(errno));

	if (lx_tsd.lxtsd_exit == 0) {
		lx_runexe(argv, edp.ed_ldentry);
		/* lx_runexe() never returns. */
		assert(0);
	}

	/*
	 * We are here because the Linux application called the exit() or
	 * exit_group() system call.  In turn the brand library did a
	 * setcontext() to jump to the thread context state we saved above.
	 */
	if (lx_tsd.lxtsd_exit == 1)
		thr_exit((void *)lx_tsd.lxtsd_exit_status);
	else
		exit(lx_tsd.lxtsd_exit_status);

	assert(0);

	/*NOTREACHED*/
	return (0);
}

/*
 * Walk back through the stack until we find the lx_emulate() frame.
 */
lx_regs_t *
lx_syscall_regs(void)
{
	/* LINTED - alignment */
	struct frame *fr = (struct frame *)_getfp();

	while (fr->fr_savpc != (uintptr_t)&lx_emulate_done) {
		fr = (struct frame *)fr->fr_savfp;
		assert(fr->fr_savpc != NULL);
	}

	return ((lx_regs_t *)((uintptr_t *)fr)[2]);
}

int
lx_lpid_to_spair(pid_t lpid, pid_t *spid, lwpid_t *slwp)
{
	pid_t pid;
	lwpid_t tid;

	if (lpid == 0) {
		pid = getpid();
		tid = thr_self();
	} else {
		if (syscall(SYS_brand, B_LPID_TO_SPAIR, lpid, &pid, &tid) < 0)
			return (-errno);

		/*
		 * If the returned pid is -1, that indicates we tried to
		 * look up the PID for init, but that process no longer
		 * exists.
		 */
		if (pid == -1)
			return (-ESRCH);
	}

	if (uucopy(&pid, spid, sizeof (pid_t)) != 0)
		return (-errno);

	if (uucopy(&tid, slwp, sizeof (lwpid_t)) != 0)
		return (-errno);

	return (0);
}

int
lx_lpid_to_spid(pid_t lpid, pid_t *spid)
{
	lwpid_t slwp;

	return (lx_lpid_to_spair(lpid, spid, &slwp));
}

char *
lx_fd_to_path(int fd, char *buf, int buf_size)
{
	char	path_proc[MAXPATHLEN];
	pid_t	pid;
	int	n;

	assert((buf != NULL) && (buf_size >= 0));

	if (fd < 0)
		return (NULL);

	if ((pid = getpid()) == -1)
		return (NULL);

	(void) snprintf(path_proc, MAXPATHLEN,
	    "/native/proc/%d/path/%d", pid, fd);

	if ((n = readlink(path_proc, buf, buf_size - 1)) == -1)
		return (NULL);
	buf[n] = '\0';

	return (buf);
}

/*
 * Create a translation routine that jumps to a particular emulation
 * module syscall.
 */
#define	IN_KERNEL_SYSCALL(name, num)		\
int						\
lx_##name(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,	\
	uintptr_t p5, uintptr_t p6)		\
{						\
	int r;					\
	lx_debug("\tsyscall %d re-vectoring to lx kernel module "	\
	    "for " #name "()", num);		\
	r = syscall(SYS_brand, B_EMULATE_SYSCALL + num, p1, p2,		\
	    p3, p4, p5, p6);			\
	return ((r == -1) ? -errno : r);		\
}

IN_KERNEL_SYSCALL(kill, 37)
IN_KERNEL_SYSCALL(brk, 45)
IN_KERNEL_SYSCALL(ustat, 62)
IN_KERNEL_SYSCALL(getppid, 64)
IN_KERNEL_SYSCALL(sysinfo, 116)
IN_KERNEL_SYSCALL(modify_ldt, 123)
IN_KERNEL_SYSCALL(adjtimex, 124)
IN_KERNEL_SYSCALL(setresuid16, 164)
IN_KERNEL_SYSCALL(setresgid16, 170)
IN_KERNEL_SYSCALL(setresuid, 208)
IN_KERNEL_SYSCALL(setresgid, 210)
IN_KERNEL_SYSCALL(gettid, 224)
IN_KERNEL_SYSCALL(tkill, 238)
IN_KERNEL_SYSCALL(futex, 240)
IN_KERNEL_SYSCALL(set_thread_area, 243)
IN_KERNEL_SYSCALL(get_thread_area, 244)
IN_KERNEL_SYSCALL(set_tid_address, 258)

static struct lx_sysent sysents[] = {
	{"nosys",	NULL,		NOSYS_NULL,	0},	/*  0 */
	{"exit",	lx_exit,	0,		1},	/*  1 */
	{"fork",	lx_fork,	0,		0},	/*  2 */
	{"read",	lx_read,	0,		3},	/*  3 */
	{"write",	write,		SYS_PASSTHRU,	3},	/*  4 */
	{"open",	lx_open,	0,		3},	/*  5 */
	{"close",	close,		SYS_PASSTHRU,	1},	/*  6 */
	{"waitpid",	lx_waitpid,	0,		3},	/*  7 */
	{"creat",	creat,		SYS_PASSTHRU,	2},	/*  8 */
	{"link",	lx_link,	0,		2},	/*  9 */
	{"unlink",	lx_unlink,	0,		1},	/* 10 */
	{"execve",	lx_execve,	0,		3},	/* 11 */
	{"chdir",	chdir,		SYS_PASSTHRU,	1},	/* 12 */
	{"time",	lx_time,	0,		1},	/* 13 */
	{"mknod",	lx_mknod,	0,		3},	/* 14 */
	{"chmod",	lx_chmod,	0,		2},	/* 15 */
	{"lchown16",	lx_lchown16,	0,		3},	/* 16 */
	{"break",	NULL,		NOSYS_OBSOLETE,	0},	/* 17 */
	{"stat",	NULL,		NOSYS_OBSOLETE,	0},	/* 18 */
	{"lseek",	lx_lseek,	0,		3},	/* 19 */
	{"getpid",	lx_getpid,	0,		0},	/* 20 */
	{"mount",	lx_mount,	0,		5},	/* 21 */
	{"umount",	lx_umount,	0,		1},	/* 22 */
	{"setuid16",	lx_setuid16,	0,		1},	/* 23 */
	{"getuid16",	lx_getuid16,	0,		0},	/* 24 */
	{"stime",	stime,		SYS_PASSTHRU,	1},	/* 25 */
	{"ptrace",	lx_ptrace,	0,		4},	/* 26 */
	{"alarm",	(int (*)())alarm, SYS_PASSTHRU,	1},	/* 27 */
	{"fstat",	NULL,		NOSYS_OBSOLETE,	0},	/* 28 */
	{"pause",	pause,		SYS_PASSTHRU,	0},	/* 29 */
	{"utime",	lx_utime,	0,		2},	/* 30 */
	{"stty",	NULL,		NOSYS_OBSOLETE,	0},	/* 31 */
	{"gtty",	NULL,		NOSYS_OBSOLETE,	0},	/* 32 */
	{"access",	access,		SYS_PASSTHRU,	2},	/* 33 */
	{"nice",	nice,		SYS_PASSTHRU,	1},	/* 34 */
	{"ftime",	NULL,		NOSYS_OBSOLETE,	0},	/* 35 */
	{"sync",	lx_sync, 	0, 		0},	/* 36 */
	{"kill",	lx_kill,	0,		2},	/* 37 */
	{"rename",	lx_rename,	0,		2},	/* 38 */
	{"mkdir",	mkdir,		SYS_PASSTHRU,	2},	/* 39 */
	{"rmdir",	lx_rmdir,	0,		1},	/* 40 */
	{"dup",		dup,		SYS_PASSTHRU,	1},	/* 41 */
	{"pipe",	lx_pipe,	0,		1},	/* 42 */
	{"times",	lx_times,	0,		1},	/* 43 */
	{"prof",	NULL,		NOSYS_OBSOLETE,	0},	/* 44 */
	{"brk",		lx_brk,		0,		1},	/* 45 */
	{"setgid16",	lx_setgid16,	0,		1},	/* 46 */
	{"getgid16",	lx_getgid16,	0,		0},	/* 47 */
	{"signal",	lx_signal,	0,		2},	/* 48 */
	{"geteuid16",	lx_geteuid16,	0,		0},	/* 49 */
	{"getegid16",	lx_getegid16,	0,		0},	/* 50 */
	{"acct",	NULL,		NOSYS_NO_EQUIV,	0},	/* 51 */
	{"umount2",	lx_umount2,	0,		2},	/* 52 */
	{"lock",	NULL,		NOSYS_OBSOLETE,	0},	/* 53 */
	{"ioctl",	lx_ioctl,	0,		3},	/* 54 */
	{"fcntl",	lx_fcntl,	0,		3},	/* 55 */
	{"mpx",		NULL,		NOSYS_OBSOLETE,	0},	/* 56 */
	{"setpgid",	lx_setpgid,	0,		2},	/* 57 */
	{"ulimit",	NULL,		NOSYS_OBSOLETE,	0},	/* 58 */
	{"olduname",	NULL,		NOSYS_OBSOLETE,	0},	/* 59 */
	{"umask",	(int (*)())umask, SYS_PASSTHRU,	1},	/* 60 */
	{"chroot",	chroot,		SYS_PASSTHRU,	1},	/* 61 */
	{"ustat",	lx_ustat,	0,		2},	/* 62 */
	{"dup2",	lx_dup2,	0,		2},	/* 63 */
	{"getppid",	lx_getppid,	0,		0},	/* 64 */
	{"getpgrp",	lx_getpgrp,	0,		0},	/* 65 */
	{"setsid",	lx_setsid,	0,		0},	/* 66 */
	{"sigaction",	lx_sigaction,	0,		3},	/* 67 */
	{"sgetmask",	NULL,		NOSYS_OBSOLETE,	0},	/* 68 */
	{"ssetmask",	NULL,		NOSYS_OBSOLETE,	0},	/* 69 */
	{"setreuid16",	lx_setreuid16,	0,		2},	/* 70 */
	{"setregid16",	lx_setregid16,	0,		2},	/* 71 */
	{"sigsuspend",	lx_sigsuspend,	0,		1},	/* 72 */
	{"sigpending",	lx_sigpending,	0,		1},	/* 73 */
	{"sethostname",	lx_sethostname,	0,		2},	/* 74 */
	{"setrlimit",	lx_setrlimit,	0,		2},	/* 75 */
	{"getrlimit",	lx_oldgetrlimit, 0,		2},	/* 76 */
	{"getrusage",	lx_getrusage,	0,		2},	/* 77 */
	{"gettimeofday", lx_gettimeofday, 0,		2},	/* 78 */
	{"settimeofday", lx_settimeofday, 0,		2},	/* 79 */
	{"getgroups16",	lx_getgroups16,	0,		2},	/* 80 */
	{"setgroups16",	lx_setgroups16,	0,		2},	/* 81 */
	{"select",	NULL,		NOSYS_OBSOLETE,	0},	/* 82 */
	{"symlink",	symlink,	SYS_PASSTHRU,	2},	/* 83 */
	{"oldlstat",	NULL,		NOSYS_OBSOLETE,	0},	/* 84 */
	{"readlink",	readlink,	SYS_PASSTHRU,	3},	/* 85 */
	{"uselib",	NULL,		NOSYS_KERNEL,	0},	/* 86 */
	{"swapon",	NULL,		NOSYS_KERNEL,	0},	/* 87 */
	{"reboot",	lx_reboot,	0,		4},	/* 88 */
	{"readdir",	lx_readdir,	0,		3},	/* 89 */
	{"mmap",	lx_mmap,	0,		6},	/* 90 */
	{"munmap",	munmap,		SYS_PASSTHRU,	2},	/* 91 */
	{"truncate",	lx_truncate,	0,		2},	/* 92 */
	{"ftruncate",	lx_ftruncate,	0,		2},	/* 93 */
	{"fchmod",	fchmod,		SYS_PASSTHRU,	2},	/* 94 */
	{"fchown16",	lx_fchown16,	0,		3},	/* 95 */
	{"getpriority",	lx_getpriority,	0,		2},	/* 96 */
	{"setpriority",	lx_setpriority,	0,		3},	/* 97 */
	{"profil",	NULL,		NOSYS_NO_EQUIV,	0},	/* 98 */
	{"statfs",	lx_statfs,	0,		2},	/* 99 */
	{"fstatfs",	lx_fstatfs,	0,		2},	/* 100 */
	{"ioperm",	NULL,		NOSYS_NO_EQUIV,	0},	/* 101 */
	{"socketcall",	lx_socketcall,	0,		2},	/* 102 */
	{"syslog",	NULL,		NOSYS_KERNEL,	0},	/* 103 */
	{"setitimer",	lx_setitimer,	0,		3},	/* 104 */
	{"getitimer",	getitimer,	SYS_PASSTHRU,	2},	/* 105 */
	{"stat",	lx_stat,	0,		2},	/* 106 */
	{"lstat",	lx_lstat,	0,		2},	/* 107 */
	{"fstat",	lx_fstat,	0,		2},	/* 108 */
	{"uname",	NULL,		NOSYS_OBSOLETE,	0},	/* 109 */
	{"oldiopl",	NULL,		NOSYS_NO_EQUIV,	0},	/* 110 */
	{"vhangup",	lx_vhangup,	0,		0},	/* 111 */
	{"idle",	NULL,		NOSYS_NO_EQUIV,	0},	/* 112 */
	{"vm86old",	NULL,		NOSYS_OBSOLETE,	0},	/* 113 */
	{"wait4",	lx_wait4,	0,		4},	/* 114 */
	{"swapoff",	NULL,		NOSYS_KERNEL,	0},	/* 115 */
	{"sysinfo",	lx_sysinfo,	0,		1},	/* 116 */
	{"ipc",		lx_ipc,		0,		5},	/* 117 */
	{"fsync",	lx_fsync,	0,		1},	/* 118 */
	{"sigreturn",	lx_sigreturn,	0,		1},	/* 119 */
	{"clone",	lx_clone,	0,		5},	/* 120 */
	{"setdomainname", lx_setdomainname, 0,		2},	/* 121 */
	{"uname",	lx_uname,	0,		1},	/* 122 */
	{"modify_ldt",	lx_modify_ldt,	0,		3},	/* 123 */
	{"adjtimex",	lx_adjtimex,	0,		1},	/* 124 */
	{"mprotect",	lx_mprotect,	0,		3},	/* 125 */
	{"sigprocmask",	lx_sigprocmask,	0,		3},	/* 126 */
	{"create_module", NULL,		NOSYS_KERNEL,	0},	/* 127 */
	{"init_module",	NULL,		NOSYS_KERNEL,	0},	/* 128 */
	{"delete_module", NULL,		NOSYS_KERNEL,	0},	/* 129 */
	{"get_kernel_syms", NULL,	NOSYS_KERNEL,	0},	/* 130 */
	{"quotactl",	NULL,		NOSYS_KERNEL,	0},	/* 131 */
	{"getpgid",	lx_getpgid,	0,		1},	/* 132 */
	{"fchdir",	fchdir,		SYS_PASSTHRU,	1},	/* 133 */
	{"bdflush",	NULL,		NOSYS_KERNEL,	0},	/* 134 */
	{"sysfs",	lx_sysfs, 	0,		3},	/* 135 */
	{"personality",	lx_personality,	0,		1},	/* 136 */
	{"afs_syscall",	NULL,		NOSYS_KERNEL,	0},	/* 137 */
	{"setfsuid16",	lx_setfsuid16,	0,		1},	/* 138 */
	{"setfsgid16",	lx_setfsgid16,	0,		1},	/* 139 */
	{"llseek",	lx_llseek,	0,		5},	/* 140 */
	{"getdents",	getdents,	SYS_PASSTHRU,	3},	/* 141 */
	{"select",	lx_select,	0,		5},	/* 142 */
	{"flock",	lx_flock,	0,		2},	/* 143 */
	{"msync",	lx_msync,	0,		3},	/* 144 */
	{"readv",	lx_readv,	0,		3},	/* 145 */
	{"writev",	lx_writev,	0,		3},	/* 146 */
	{"getsid",	lx_getsid,	0,		1},	/* 147 */
	{"fdatasync",	lx_fdatasync,	0,		1},	/* 148 */
	{"sysctl",	lx_sysctl,	0,		1},	/* 149 */
	{"mlock",	lx_mlock,	0,		2},	/* 150 */
	{"munlock",	lx_munlock,	0,		2},	/* 151 */
	{"mlockall",	lx_mlockall,	0,		1},	/* 152 */
	{"munlockall",	lx_munlockall,	0,		0},	/* 153 */
	{"sched_setparam", lx_sched_setparam,	0,	2},	/* 154 */
	{"sched_getparam", lx_sched_getparam,	0,	2},	/* 155 */
	{"sched_setscheduler", lx_sched_setscheduler, 0, 3},	/* 156 */
	{"sched_getscheduler", lx_sched_getscheduler, 0, 1},	/* 157 */
	{"sched_yield",	(int (*)())yield, SYS_PASSTHRU,	0},	/* 158 */
	{"sched_get_priority_max", lx_sched_get_priority_max, 0, 1}, /* 159 */
	{"sched_get_priority_min", lx_sched_get_priority_min, 0, 1}, /* 160 */
	{"sched_rr_get_interval", lx_sched_rr_get_interval, 0,	2},  /* 161 */
	{"nanosleep",	nanosleep,	SYS_PASSTHRU,	2},	/* 162 */
	{"mremap",	NULL,		NOSYS_NO_EQUIV,	0},	/* 163 */
	{"setresuid16",	lx_setresuid16,	0,		3},	/* 164 */
	{"getresuid16",	lx_getresuid16,	0,		3},	/* 165 */
	{"vm86",	NULL,		NOSYS_NO_EQUIV,	0},	/* 166 */
	{"query_module", lx_query_module, NOSYS_KERNEL,	5},	/* 167 */
	{"poll",	lx_poll,	0,		3},	/* 168 */
	{"nfsservctl",	NULL,		NOSYS_KERNEL,	0},	/* 169 */
	{"setresgid16",	lx_setresgid16,	0,		3},	/* 170 */
	{"getresgid16",	lx_getresgid16,	0,		3},	/* 171 */
	{"prctl",	NULL,		NOSYS_UNDOC,	0},	/* 172 */
	{"rt_sigreturn", lx_rt_sigreturn, 0,		0},	/* 173 */
	{"rt_sigaction", lx_rt_sigaction, 0,		4},	/* 174 */
	{"rt_sigprocmask", lx_rt_sigprocmask, 0,	4},	/* 175 */
	{"rt_sigpending", lx_rt_sigpending, 0,		2},	/* 176 */
	{"rt_sigtimedwait", lx_rt_sigtimedwait,	0,	4},	/* 177 */
	{"sigqueueinfo", NULL,		NOSYS_UNDOC,	0},	/* 178 */
	{"rt_sigsuspend", lx_rt_sigsuspend, 0,		2},	/* 179 */
	{"pread64",	lx_pread64,	0,		5},	/* 180 */
	{"pwrite64",	lx_pwrite64,	0,		5},	/* 181 */
	{"chown16",	lx_chown16,	0,		3},	/* 182 */
	{"getcwd",	lx_getcwd,	0,		2},	/* 183 */
	{"capget",	NULL,		NOSYS_NO_EQUIV,	0},	/* 184 */
	{"capset",	NULL,		NOSYS_NO_EQUIV,	0},	/* 185 */
	{"sigaltstack",	lx_sigaltstack,	0,		2},	/* 186 */
	{"sendfile",	lx_sendfile,	0,		4},	/* 187 */
	{"getpmsg",	NULL,		NOSYS_OBSOLETE,	0},	/* 188 */
	{"putpmsg",	NULL,		NOSYS_OBSOLETE,	0},	/* 189 */
	{"vfork",	lx_vfork,	0,		0},	/* 190 */
	{"getrlimit",	lx_getrlimit,	0,		2},	/* 191 */
	{"mmap2",	lx_mmap2,	EBP_HAS_ARG6,	6},	/* 192 */
	{"truncate64",	lx_truncate64,	0,		3},	/* 193 */
	{"ftruncate64",	lx_ftruncate64,	0,		3},	/* 194 */
	{"stat64",	lx_stat64,	0,		2},	/* 195 */
	{"lstat64",	lx_lstat64,	0,		2},	/* 196 */
	{"fstat64",	lx_fstat64,	0,		2},	/* 197 */
	{"lchown",	lchown,		SYS_PASSTHRU,	3},	/* 198 */
	{"getuid",	(int (*)())getuid, SYS_PASSTHRU, 0},	/* 199 */
	{"getgid",	(int (*)())getgid, SYS_PASSTHRU, 0},	/* 200 */
	{"geteuid",	lx_geteuid,	0,		0},	/* 201 */
	{"getegid",	lx_getegid,	0,		0},	/* 202 */
	{"setreuid",	setreuid,	SYS_PASSTHRU,	0},	/* 203 */
	{"setregid",	setregid,	SYS_PASSTHRU,	0},	/* 204 */
	{"getgroups",	getgroups,	SYS_PASSTHRU,	2},	/* 205 */
	{"setgroups",	lx_setgroups,	0,		2},	/* 206 */
	{"fchown",	lx_fchown,	0,		3},	/* 207 */
	{"setresuid",	lx_setresuid,	0,		3},	/* 208 */
	{"getresuid",	lx_getresuid,	0,		3},	/* 209 */
	{"setresgid",	lx_setresgid,	0,		3},	/* 210 */
	{"getresgid",	lx_getresgid,	0,		3},	/* 211 */
	{"chown",	lx_chown,	0,		3},	/* 212 */
	{"setuid",	setuid,		SYS_PASSTHRU,	1},	/* 213 */
	{"setgid",	setgid,		SYS_PASSTHRU,	1},	/* 214 */
	{"setfsuid",	lx_setfsuid,	0,		1},	/* 215 */
	{"setfsgid",	lx_setfsgid,	0,		1},	/* 216 */
	{"pivot_root",	NULL,		NOSYS_KERNEL,	0},	/* 217 */
	{"mincore",	mincore,	SYS_PASSTHRU,	3},	/* 218 */
	{"madvise",	lx_madvise,	0,		3},	/* 219 */
	{"getdents64",	lx_getdents64,	0,		3},	/* 220 */
	{"fcntl64",	lx_fcntl64,	0,		3},	/* 221 */
	{"tux",		NULL,		NOSYS_NO_EQUIV,	0},	/* 222 */
	{"security",	NULL,		NOSYS_NO_EQUIV,	0},	/* 223 */
	{"gettid",	lx_gettid,	0,		0},	/* 224 */
	{"readahead",	NULL,		NOSYS_NO_EQUIV,	0},	/* 225 */
	{"setxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 226 */
	{"lsetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 227 */
	{"fsetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 228 */
	{"getxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 229 */
	{"lgetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 230 */
	{"fgetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 231 */
	{"listxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 232 */
	{"llistxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 233 */
	{"flistxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 234 */
	{"removexattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 235 */
	{"lremovexattr", NULL,		NOSYS_NO_EQUIV,	0},	/* 236 */
	{"fremovexattr", NULL,		NOSYS_NO_EQUIV,	0},	/* 237 */
	{"tkill",	lx_tkill,	0,		2},	/* 238 */
	{"sendfile64",	lx_sendfile64,	0,		4},	/* 239 */
	{"futex",	lx_futex,	EBP_HAS_ARG6,	6},	/* 240 */
	{"sched_setaffinity",	lx_sched_setaffinity,	0, 3},	/* 241 */
	{"sched_getaffinity",	lx_sched_getaffinity,	0, 3},	/* 242 */
	{"set_thread_area", lx_set_thread_area,	0,	1},	/* 243 */
	{"get_thread_area", lx_get_thread_area,	0,	1},	/* 244 */
	{"io_setup",	NULL,		NOSYS_NO_EQUIV,	0},	/* 245 */
	{"io_destroy",	NULL,		NOSYS_NO_EQUIV,	0},	/* 246 */
	{"io_getevents", NULL,		NOSYS_NO_EQUIV,	0},	/* 247 */
	{"io_submit",	NULL,		NOSYS_NO_EQUIV,	0},	/* 248 */
	{"io_cancel",	NULL,		NOSYS_NO_EQUIV,	0},	/* 249 */
	{"fadvise64",	NULL,		NOSYS_UNDOC,	0},	/* 250 */
	{"nosys",	NULL,		0,		0},	/* 251 */
	{"group_exit",	lx_group_exit,	0,		1},	/* 252 */
	{"lookup_dcookie", NULL,	NOSYS_NO_EQUIV,	0},	/* 253 */
	{"epoll_create", NULL,		NOSYS_NO_EQUIV,	0},	/* 254 */
	{"epoll_ctl",	NULL,		NOSYS_NO_EQUIV,	0},	/* 255 */
	{"epoll_wait",	NULL,		NOSYS_NO_EQUIV,	0},	/* 256 */
	{"remap_file_pages", NULL,	NOSYS_NO_EQUIV,	0},	/* 257 */
	{"set_tid_address", lx_set_tid_address,	0,	1},	/* 258 */
	{"timer_create", NULL,		NOSYS_UNDOC,	0},	/* 259 */
	{"timer_settime", NULL,		NOSYS_UNDOC,	0},	/* 260 */
	{"timer_gettime", NULL,		NOSYS_UNDOC,	0},	/* 261 */
	{"timer_getoverrun", NULL,	NOSYS_UNDOC,	0},	/* 262 */
	{"timer_delete", NULL,		NOSYS_UNDOC,	0},	/* 263 */
	{"clock_settime", lx_clock_settime,	0,	2},	/* 264 */
	{"clock_gettime", lx_clock_gettime,	0,	2},	/* 265 */
	{"clock_getres", lx_clock_getres,	0,	2},	/* 266 */
	{"clock_nanosleep", lx_clock_nanosleep,	0,	4},	/* 267 */
	{"statfs64",	lx_statfs64,	0,		2},	/* 268 */
	{"fstatfs64",	lx_fstatfs64,	0,		2},	/* 269 */
	{"tgkill",	lx_tgkill,	0,		3},	/* 270 */

	/* The following system calls only exist in kernel 2.6 and greater */
	{"utimes",	utimes,		SYS_PASSTHRU,	2},	/* 271 */
	{"fadvise64_64", NULL,		NOSYS_NULL,	0},	/* 272 */
	{"vserver",	NULL,		NOSYS_NULL,	0},	/* 273 */
	{"mbind",	NULL,		NOSYS_NULL,	0},	/* 274 */
	{"get_mempolicy", NULL,		NOSYS_NULL,	0},	/* 275 */
	{"set_mempolicy", NULL,		NOSYS_NULL,	0},	/* 276 */
	{"mq_open",	NULL,		NOSYS_NULL,	0},	/* 277 */
	{"mq_unlink",	NULL,		NOSYS_NULL,	0},	/* 278 */
	{"mq_timedsend", NULL,		NOSYS_NULL,	0},	/* 279 */
	{"mq_timedreceive", NULL,	NOSYS_NULL,	0},	/* 280 */
	{"mq_notify",	NULL,		NOSYS_NULL,	0},	/* 281 */
	{"mq_getsetattr", NULL,		NOSYS_NULL,	0},	/* 282 */
	{"kexec_load",	NULL,		NOSYS_NULL,	0},	/* 283 */
	{"waitid",	lx_waitid,	0,		4},	/* 284 */
	{"sys_setaltroot", NULL,	NOSYS_NULL,	0},	/* 285 */
	{"add_key",	NULL,		NOSYS_NULL,	0},	/* 286 */
	{"request_key",	NULL,		NOSYS_NULL,	0},	/* 287 */
	{"keyctl",	NULL,		NOSYS_NULL,	0},	/* 288 */
	{"ioprio_set",	NULL,		NOSYS_NULL,	0},	/* 289 */
	{"ioprio_get",	NULL,		NOSYS_NULL,	0},	/* 290 */
	{"inotify_init", NULL,		NOSYS_NULL,	0},	/* 291 */
	{"inotify_add_watch", NULL,	NOSYS_NULL,	0},	/* 292 */
	{"inotify_rm_watch", NULL,	NOSYS_NULL,	0},	/* 293 */
	{"migrate_pages", NULL,		NOSYS_NULL,	0},	/* 294 */
	{"openat",	lx_openat,	0,		4},	/* 295 */
	{"mkdirat",	lx_mkdirat,	0,		3},	/* 296 */
	{"mknodat",	lx_mknodat,	0,		4},	/* 297 */
	{"fchownat",	lx_fchownat,	0,		5},	/* 298 */
	{"futimesat",	lx_futimesat,	0,		3},	/* 299 */
	{"fstatat64",	lx_fstatat64,	0,		4},	/* 300 */
	{"unlinkat",	lx_unlinkat,	0,		3},	/* 301 */
	{"renameat",	lx_renameat,	0,		4},	/* 302 */
	{"linkat",	lx_linkat,	0,		5},	/* 303 */
	{"symlinkat",	lx_symlinkat,	0,		3},	/* 304 */
	{"readlinkat",	lx_readlinkat,	0,		4},	/* 305 */
	{"fchmodat",	lx_fchmodat,	0,		4},	/* 306 */
	{"faccessat",	lx_faccessat,	0,		4},	/* 307 */
	{"pselect6",	NULL,		NOSYS_NULL,	0},	/* 308 */
	{"ppoll",	NULL,		NOSYS_NULL,	0},	/* 309 */
	{"unshare",	NULL,		NOSYS_NULL,	0},	/* 310 */
	{"set_robust_list", NULL,	NOSYS_NULL,	0},	/* 311 */
	{"get_robust_list", NULL,	NOSYS_NULL,	0},	/* 312 */
	{"splice",	NULL,		NOSYS_NULL,	0},	/* 313 */
	{"sync_file_range", NULL,	NOSYS_NULL,	0},	/* 314 */
	{"tee",		NULL,		NOSYS_NULL,	0},	/* 315 */
	{"vmsplice",	NULL,		NOSYS_NULL,	0},	/* 316 */
	{"move_pages",	NULL,		NOSYS_NULL,	0},	/* 317 */
};