Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/old/linuxemu.old/linuxemu.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <ureg.h>
#include "linux.h"
#include "linuxsys.h"

typedef struct Ureg Ureg;

static void	maincall(ElfEx *ex, ulong, char**);
static void linuxexec(int , char **);
static int linuxnote(void *v, char *msg);

char	**emuargv;
int debug;
Thread *threadp;

void
usage(void)
{
	fprint(2, "usage: %s [-d] [-u uid] [-g gid] linuxprog args...\n", argv0);
	exits("usage");
}

void
main(int argc, char *argv[])
{
	uchar ninestack[16 * 1024];
	char *eargv[10];
	char **p;
	char *a;
	int n;
	Thread thread;

	n = sizeof(eargv)/sizeof(eargv[0]);

	set9stack(ninestack + sizeof(ninestack));

	threadp = &thread;
	memset(threadp, 0, sizeof(*threadp));

	threadp->tid = threadp->tgid = getpid();
	threadp->ptid = getppid();
	threadp->cleartidaddr = nil;

	/* can be overriden by -u and -g */
	threadp->uid = 1;
	threadp->gid = 1;

	threadp->umask = 000;

	memset(&threadp->ss, 0, sizeof(threadp->ss));

	/*
	 * we build the emuargv array here. this array is put before
	 * the supplied argv on execve() syscall.
	 */
	p = emuargv = eargv;

	/*
	 * emuargv[0] needs to be absolute path because the emulated
	 * program could chdir()ed away.
	 */
	if(*argv[0]!='/'){
		static char buf[256];
		snprint(buf, sizeof(buf), "%s/%s", getwd(buf, sizeof(buf)), argv[0]);
		*p++ = buf;
	} else {
		*p++ = argv[0];
	}
	n--;

	ARGBEGIN{
	case 'd':
		debug = 1;
		if(n > 1){
			*p++ = "-d";
			n--;
		}
		break;
	case 'u':
		a = EARGF(usage());
		threadp->uid = atoi(a);
		if(n > 2){
			*p++ = "-u";
			*p++ = a;
			n -= 2;
		}
		break;
	case 'g':
		a = EARGF(usage());
		threadp->gid = atoi(a);
		if(n > 2){
			*p++ = "-g";
			*p++ = a;
			n -= 2;
		}
		break;
	default:
		usage();
	}ARGEND

	/*
	 * terminate emuargv here. further arguments are passed to the
	 * emulated program.
	 */
	*p = nil;

	if(argc < 1)
		usage();

	mmapinit();
	linuxexec(argc, argv);
	abort();	// not reached
}

static void
linuxexec(int argc, char **argv)
{
	int fd;
	int l, i;

	char *name;
	char ident[256];

	name = argv[0];

	if((fd = open(name, OREAD)) < 0){
		fprint(2, "cant open executable: %r\n");
		exits("open");
	}
	if((l = read(fd, ident, sizeof(ident)-1)) < 4){
		close(fd);
		fprint(2, "cant read executable: %r\n");
		exits("read");
	}
	ident[l] = '\0';
	close(fd);

	if(memcmp(ident, "#!", 2)==0){
		/* this is a interpreted file */
		int n, i;
		char *iargv[1024];
		char *p;

		name = ident+2;
		for(p=name; *p && *p!='\n'; p++)
			;
		*p = '\0';

		n = (sizeof(iargv)/sizeof(iargv[0])) - argc;
		if(n < 1)
			n = 1;
		n = tokenize(name, iargv, n);
		if(n < 1){
			fprint(2, "bad format");
			exits("bad format");
		}
		for(i=0; i<argc; i++)
			iargv[n++] = argv[i];
		iargv[n] = 0;
		linuxexec(n, iargv);
		abort();
	}

	DPRINT("linuxexec(%d, [", argc);
	for(i=0; i<argc; i++){
		DPRINT((i>0)?", %s": "%s", argv[i]);
	}
	DPRINT("], ...)...\n");

	if(memcmp(ident, "\x7fELF", 4)==0){
		/* this is a elf binary */
		ElfEx ex;
		if(loadelf(name, &ex) < 0){
			fprint(2, "%s: %r\n", name);
			exits("loadelf");
		}
		maincall(&ex, argc, argv);
		abort();
	} else {
		/* assume native format */
		exec(name, argv);
		fprint(2, "cant execute: %r\n");
		exits("exec");
	}
}

enum {
	AT_NULL,
	AT_IGNORE,
	AT_EXECFD,
	AT_PHDR,
	AT_PHENT,
	AT_PHNUM,
	AT_PAGESZ,
	AT_BASE,
	AT_FLAGS,
	AT_ENTRY,
	AT_NOTELF,
	AT_UID,
	AT_EUID,
	AT_GID,
	AT_EGID,
	AT_PLATFORM,
	AT_HWCAP,
	AT_CLKTCK,
	AT_SECURE = 23,
};

/*
 * set up the argument stack 
 * for linux and do the jmp.
 * this should not return, since 
 * what we're jumping to is supposed
 * to call exit.
 *
 * we use jumpstack, provided by stack.s
 *
 * it expects 
 *
 * high
 *	| aux val[n-1]
 *	| aux key[n-1]
 *	| ...
 *	| aux val[1]
 *	| aux key[1]
 *	| aux val[0]
 *	| aux key[0]
 *	|----
 *	| nil
 *	|----
 *	| envp[n-1]
 *	| ...
 *	| envp[1]
 *	| envp[0]
 *	|----
 *	| nil
 *	|----
 *	| argv[n-1]
 *	| ...
 *	| argv[1]
 *	| argv[0]
 *	|----
 *	| argc
 *	|----	← sp
 * low
 *
 */
static void
maincall(ElfEx *ex, ulong argc, char **argv)
{
	char **envp;
	ulong *stack;
	ulong *p;
	ulong f;
	int i, n;

	/*
	 * calculate the size we need on stack
	 */
	n = 8;					// padding
	n += (argc+1)*sizeof(char*);	// argv + nil
	n += sizeof(ulong);			// argc
	n += 13*(2*sizeof(ulong));	// aux

	stack = (ulong*)((uchar*)allocstack(2 * 1024 * 1024) - n - 4096);
	p = (ulong*)stack;

	envp = readenv((char*)p + n, 4096);

	// argc
	*p++ = argc;

	// argv[]
	for(i=0; i<argc; i++)
		*p++ = (ulong) argv[i];
	*p++ = 0;

	// envp[]
	for(i=0; envp[i]; i++)
		*p++ = (ulong)envp[i];
	*p++ = 0;

	// aux
#define AUXENT(k, v)  {p[0]=k; p[1]=v; p+=2;}
	AUXENT(AT_PAGESZ, PAGE_SIZE);
	AUXENT(AT_CLKTCK, 100);
	AUXENT(AT_PHDR, ex->phdr);
	AUXENT(AT_PHENT, ex->phent);
	AUXENT(AT_PHNUM, ex->phnum);
	AUXENT(AT_BASE, ex->ibase);
	AUXENT(AT_FLAGS, 0);
	AUXENT(AT_ENTRY, ex->entry);
	AUXENT(AT_UID, threadp->uid);
	AUXENT(AT_EUID, threadp->uid);
	AUXENT(AT_GID, threadp->gid);
	AUXENT(AT_EGID, threadp->gid);
	AUXENT(AT_NULL, 0);
	USED(p);
#undef AUXENT


	DPRINT("entry=%lux\n", ex->ientry);

	/* disable FPU faults */
	f = getfcr();
	f &= ~FPINVAL;
	setfcr(f);

	/* install ``syscall'' handler */
	atnotify(linuxnote, 1);

	/* go! */
	jumpstack(ex->ientry, stack);
	abort();
}

static void
clinote(Ureg *ureg)
{
	jmp_buf jmp;
	ulong pc;
	ulong sp;
	ulong ax;

	pc = ureg->pc;
	sp = ureg->sp;
	ax = ureg->ax;

	if(!setjmp(jmp))
		notejmp(ureg, jmp, 1);

	ureg->pc = pc;
	ureg->sp = sp;
	ureg->ax = ax;
}

static int
linuxnote(void *v, char *msg)
{
	Ureg *ureg;

	ureg = v;

	if(!threadp->pid)
		return 0;

	sigdisable(&threadp->ss);
	if(strstr(msg, "general protection violation") == nil){

		/* doesnt look like a syscall, check for signal */
		if(!signote(&threadp->ss, msg)){
			sigenable(&threadp->ss);
			return 0;
		}

		clinote(ureg);
	} else {
		uchar *x;
		int n;

		x = (uchar*)ureg->pc;
		if(x[0] != 0xCD || x[1] != 0x80){	/* INT 0x80 */
			sigenable(&threadp->ss);
			return 0;
		}

		clinote(ureg);
		n = (int)ureg->ax;
		if(n < 0 || n >= LMAXSYSCALL){
			fprint(2, "[%d] syscall ???/%d out of range\n", 
				threadp->pid, n);
			ureg->ax = -1;
		} else {
			void (*f)(Ureg*);
			char *s;

			s = syscallname[n];
			f = syscalltab[n];

			if(f == nil){
				fprint(2, "[%d] sycall %s/%d not implemented\n", 
					threadp->pid, s, n);
				ureg->ax = -1;
			} else {
				DPRINT("[%d] syscall %s/%d...", threadp->pid, s, n);
				f(ureg);
				DPRINT("-> %d/0x%lux/0%o\n",
					(int)ureg->ax, (ulong)ureg->ax, (int)ureg->ax);
			}
		}

		/* skip INT 0x80 */
		ureg->pc += 2;
	}

	sigenable(&threadp->ss);
	jumpureg(ureg);

	abort();

	return 1;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.