#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;
}
|