#include #include #include #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; i0)?", %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; iphdr); 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; }