vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

vxrun.c (11583B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdarg.h>
      4 #include <string.h>
      5 #include <assert.h>
      6 #include <unistd.h>
      7 #include <errno.h>
      8 #include <pthread.h>
      9 #include <sys/stat.h>
     10 #include <sys/time.h>
     11 #include <sys/wait.h>
     12 #include <fcntl.h>
     13 #include <setjmp.h>
     14 #include "vx32.h"
     15 #include "args.h"
     16 #include "libvxc_dat.h"
     17 
     18 #define syscall xxxsyscall // FIXME
     19 #include "libvxc/syscall.h"
     20 
     21 #define V (void*)(uintptr_t)
     22 
     23 const char *argv0;
     24 
     25 extern int vx_elfbigmem;
     26 
     27 static const char *progname;
     28 static pid_t progpid;
     29 
     30 static int verbose = 0;
     31 
     32 static int runreps = 1;
     33 static jmp_buf exitbuf;
     34 
     35 static void fatal(const char *fmt, ...)
     36 {
     37 	va_list ap;
     38 	fprintf(stderr, "%s: fatal error: ", progname);
     39 	va_start(ap, fmt);
     40 	vfprintf(stderr, fmt, ap);
     41 	va_end(ap);
     42 	fputc('\n', stderr);
     43 	exit(2);
     44 }
     45 
     46 static void dumpregs(struct vxproc *p)
     47 {
     48 	struct vxcpu *c = p->cpu;
     49 
     50 	fprintf(stderr, "eax %08x  ecx %08x  edx %08x  ebx %08x\n",
     51 		c->reg[EAX], c->reg[ECX], c->reg[EDX], c->reg[EBX]);
     52 	fprintf(stderr, "esp %08x  ebp %08x  esi %08x  edi %08x\n",
     53 		c->reg[ESP], c->reg[EBP], c->reg[ESI], c->reg[EDI]);
     54 	fprintf(stderr, "eip %08x  eflags %08x\n",
     55 		c->eip, c->eflags);
     56 
     57 //	for (int i = 0; i < 8; i++) {
     58 //		int32_t *val = r.xmm[i].i32;
     59 //		fprintf(stderr, "xmm%d %08x%08x%08x%08x\n",
     60 //			i, val[3], val[2], val[1], val[0]);
     61 //	}
     62 }
     63 
     64 static uint32_t mode2vxc(uint32_t st)
     65 {
     66 	uint32_t vxc = st & 0x0777;  // At least we agree on this!
     67 	if (st & S_ISVTX)
     68 		vxc |= VXC_S_ISVTX;
     69 	if (st & S_ISGID)
     70 		vxc |= VXC_S_ISGID;
     71 	if (st & S_ISUID)
     72 		vxc |= VXC_S_ISUID;
     73 	switch (st & S_IFMT) {
     74 	case S_IFREG:
     75 		vxc |= VXC_S_IFREG;
     76 		break;
     77 	case S_IFDIR:
     78 		vxc |= VXC_S_IFDIR;
     79 		break;
     80 	case S_IFLNK:
     81 		vxc |= VXC_S_IFLNK;
     82 		break;
     83 	case S_IFSOCK:
     84 		vxc |= VXC_S_IFSOCK;
     85 		break;
     86 	case S_IFIFO:
     87 		vxc |= VXC_S_IFIFO;
     88 		break;
     89 	case S_IFBLK:
     90 		vxc |= VXC_S_IFBLK;
     91 		break;
     92 	default:
     93 	case S_IFCHR:
     94 		vxc |= VXC_S_IFCHR;
     95 		break;
     96 	}
     97 	return vxc;
     98 }
     99 
    100 static void stat2vxc(struct vxc_stat *vst, struct stat *st)
    101 {
    102 	vst->dev = st->st_dev;
    103 	vst->ino = st->st_ino;
    104 	vst->mode = mode2vxc(st->st_mode);
    105 	vst->nlink = st->st_nlink;
    106 	vst->uid = st->st_uid;
    107 	vst->gid = st->st_gid;
    108 	vst->rdev = st->st_rdev;
    109 	vst->blksize = st->st_blksize;
    110 	vst->blocks = st->st_blocks;
    111 	vst->size = st->st_size;
    112 	vst->atime = st->st_atime;
    113 	vst->mtime = st->st_mtime;
    114 	vst->ctime = st->st_ctime;
    115 }
    116 
    117 static int checkstring(vxmem *mem, char *base, uint32_t addr)
    118 {
    119 	uint32_t eaddr;
    120 
    121 	for (;;) {
    122 		if (!vxmem_checkperm(mem, addr, 1, VXPERM_READ, NULL))
    123 			return 0;
    124 		eaddr = (addr + 4096) & ~(4096-1);
    125 		if (memchr(base + addr, 0, eaddr - addr))
    126 			return 1;
    127 		addr = eaddr;
    128 	}
    129 }
    130 
    131 static int doexec(vxproc*, char*, uint32_t, uint32_t, uint32_t);
    132 
    133 int trace;
    134 
    135 #define RET proc->cpu->reg[EAX]
    136 #define NUM proc->cpu->reg[EAX]
    137 #define ARG1 proc->cpu->reg[EDX]
    138 #define ARG2 proc->cpu->reg[ECX]
    139 #define ARG3 proc->cpu->reg[EBX]
    140 #define ARG4 proc->cpu->reg[EDI]
    141 #define ARG5 proc->cpu->reg[ESI]
    142 
    143 static void dosyscall(vxproc *proc)
    144 {
    145 	int fd, p[2], *vp, ret, mode, umode;
    146 	uint32_t addr, saddr, oaddr;
    147 	int len;
    148 	vxmmap *m;
    149 	struct stat st;
    150 	uint32_t inc;
    151 	uint32_t secs;
    152 	
    153 	m = vxmem_map(proc->mem, 0);
    154 
    155 	switch (NUM) {
    156 	case VXSYSEXIT:
    157 		if (ARG1 != 0 || runreps == 1)
    158 			exit(ARG1);
    159 		longjmp(exitbuf, 1);	// back for more repetitions...
    160 	
    161 	case VXSYSBRK:
    162 		addr = ARG1;
    163 		inc = 1<<20;
    164 		addr = (addr + inc - 1) & ~(inc - 1);
    165 		oaddr = m->size;
    166 		if (addr == oaddr) {
    167 			ret = 0;
    168 			break;
    169 		}
    170 		ret = 0;
    171 		if (addr > m->size)
    172 			ret = vxmem_resize(proc->mem, addr);
    173 		if (trace)
    174 			fprintf(stderr, "sbrk %p -> %p / %p; %d\n", V oaddr, V addr, V ARG1, ret);
    175 		if (ret < 0)
    176 			fprintf(stderr, "warning: sbrk failed. caller will be unhappy!\n");
    177 		if (ret >= 0) {
    178 			if (addr > oaddr)
    179 				ret = vxmem_setperm(proc->mem, oaddr, addr - oaddr, VXPERM_READ|VXPERM_WRITE);
    180 			if(ret < 0)
    181 				fprintf(stderr, "setperm is failing! %p + %p > %p ? \n", V oaddr, V(addr - oaddr), V m->size);
    182 		}
    183 		break;
    184 
    185 	case VXSYSREAD:
    186 		fd = ARG1;
    187 		addr = ARG2;
    188 		len = ARG3;
    189 		if (!vxmem_checkperm(proc->mem, addr, len, VXPERM_WRITE, NULL))
    190 			fatal("bad arguments to read");
    191 		ret = read(fd, (char*)m->base + addr, len);
    192 		break;
    193 	
    194 	case VXSYSWRITE:
    195 		fd = ARG1;
    196 		addr = ARG2;
    197 		len = ARG3;
    198 		if (!vxmem_checkperm(proc->mem, addr, len, VXPERM_READ, NULL))
    199 			fatal("bad arguments to write");
    200 		ret = write(fd, (char*)m->base + addr, len);
    201 		break;
    202 	
    203 	case VXSYSSTAT:
    204 		addr = ARG1;
    205 		saddr = ARG2;
    206 		if (!checkstring(proc->mem, m->base, addr) ||
    207 		    !vxmem_checkperm(proc->mem, saddr, sizeof(struct vxc_stat), VXPERM_WRITE, NULL)){
    208 		einval:
    209 			RET = -EINVAL;
    210 			goto out;
    211 		}
    212 		if ((ret = stat((char*)m->base + addr, &st)) >= 0)
    213 			stat2vxc((struct vxc_stat*)((char*)m->base + saddr), &st);
    214 		if (trace)
    215 			fprintf(stderr, "stat %x/%s => %d\n", addr, (char*)m->base+addr, ret);
    216 		break;
    217 
    218 	case VXSYSFSTAT:
    219 		fd = ARG1;
    220 		saddr = ARG2;
    221 		if (!vxmem_checkperm(proc->mem, saddr, sizeof(struct vxc_stat), VXPERM_WRITE, NULL)){
    222 			RET = -EINVAL;
    223 			goto out;
    224 		}
    225 		if ((ret = fstat(fd, &st)) >= 0)
    226 			stat2vxc((struct vxc_stat*)((char*)m->base + saddr), &st);
    227 		if (trace)
    228 			fprintf(stderr, "fstat %d => %d\n", fd, ret);
    229 		break;
    230 
    231 	case VXSYSREMOVE:
    232 		addr = ARG1;
    233 		if (!checkstring(proc->mem, m->base, addr))
    234 			goto einval;
    235 		char *name = (char*)m->base+addr;
    236 		if (stat(name, &st) >= 0 && S_ISDIR(st.st_mode))
    237 			ret = rmdir(name);
    238 		else
    239 			ret = unlink(name);
    240 		break;
    241 		
    242 	case VXSYSOPEN:
    243 		addr = ARG1;
    244 		mode = ARG2;
    245 		umode = mode&3;
    246 		if(mode & VXC_O_CREAT)
    247 			umode |= O_CREAT;
    248 		if(mode & VXC_O_EXCL)
    249 			umode |= O_EXCL;
    250 		if(mode & VXC_O_NOCTTY)
    251 			umode |= O_NOCTTY;
    252 		if(mode & VXC_O_TRUNC)
    253 			umode |= O_TRUNC;
    254 		if(mode & VXC_O_APPEND)
    255 			umode |= O_APPEND;
    256 		if(mode & VXC_O_NONBLOCK)
    257 			umode |= O_NONBLOCK;
    258 		if(mode & VXC_O_SYNC)
    259 			umode |= O_SYNC;
    260 		if (!checkstring(proc->mem, m->base, addr))
    261 			goto einval;
    262 		ret = open((char*)m->base+addr, umode, ARG3);
    263 		if(trace)
    264 			fprintf(stderr, "open %s %#x %#o => %d\n", (char*)m->base+addr, ARG2, ARG3, ret);
    265 		break;
    266 
    267 	case VXSYSMKDIR:
    268 		addr = ARG1;
    269 		if (!checkstring(proc->mem, m->base, addr))
    270 			goto einval;
    271 		ret = mkdir((char*)m->base+addr, ARG2);
    272 		if (trace)
    273 			fprintf(stderr, "mkdir %s %#o => %d\n", (char*)m->base+addr, ARG2, ret);
    274 		break;
    275 
    276 	case VXSYSCLOSE:
    277 		fd = ARG1;
    278 		if (fd < 0)
    279 			goto einval;
    280 		ret = close(fd);
    281 		break;
    282 	
    283 	case VXSYSLSEEK:
    284 		ret = lseek(ARG1, (int32_t)ARG2, ARG3);
    285 		break;
    286 
    287 	case VXSYSTIME:
    288 		addr = ARG1;
    289 		if (!vxmem_checkperm(proc->mem, addr, sizeof(struct vxc_timeval), VXPERM_WRITE, NULL))
    290 			goto einval;
    291 		struct timeval tv;
    292 		gettimeofday(&tv, NULL);
    293 		struct vxc_timeval *vtv = (void*)((char*)m->base + addr);
    294 		vtv->tv_sec = tv.tv_sec;
    295 		vtv->tv_usec = tv.tv_usec;
    296 		ret = 0;
    297 		break;
    298 
    299 	case VXSYSFCNTL:
    300 		// TODO: check better
    301 		ret = fcntl(ARG1, ARG2, ARG3);
    302 		if(trace)
    303 			fprintf(stderr, "fcntl %d %d %d => %d\n", ARG1, ARG2, ARG3, ret);
    304 		break;
    305 	
    306 	case VXSYSDUP:
    307 		if(ARG2 == -1)
    308 			ret = dup(ARG1);
    309 		else
    310 			ret = dup2(ARG1, ARG2);
    311 		break;
    312 
    313 	case VXSYSFORK:
    314 		vxmem_unmap(proc->mem, m);
    315 		vxmem_unmap(proc->mem, proc->mem->mapped);
    316 		proc->mem->mapped = NULL;
    317 
    318 		vxmem *nmem = vxmem_chunk_copy(proc->mem);
    319 		if (nmem == NULL) {
    320 			RET = -errno;
    321 			return;
    322 		}
    323 		ret = fork();
    324 		if (ret < 0)
    325 			ret = -errno;
    326 		if (ret == 0) {
    327 			vxmem_free(proc->mem);
    328 			proc->mem = nmem;
    329 		} else {
    330 			vxmem_free(nmem);
    331 		}
    332 		RET = ret;
    333 		return;
    334 	
    335 	case VXSYSWAITPID:
    336 		addr = ARG2;
    337 		if (addr && !vxmem_checkperm(proc->mem, addr, 4, VXPERM_WRITE, NULL))
    338 			goto einval;
    339 		ret = waitpid(ARG1, addr ? (int*)((char*)m->base+addr) : 0, ARG3);
    340 		break;
    341 
    342 	case VXSYSEXEC:
    343 		ret = doexec(proc, m->base, ARG1, ARG2, ARG3);
    344 		break;
    345 
    346 	case VXSYSPIPE:
    347 		addr = ARG1;
    348 		if (!vxmem_checkperm(proc->mem, addr, 8, VXPERM_WRITE, NULL))
    349 			goto einval;
    350 		ret = pipe(p);
    351 		if (ret >= 0) {
    352 			vp = (int*)((char*)m->base + addr);
    353 			vp[0] = p[0];
    354 			vp[1] = p[1];
    355 		}
    356 		break;
    357 	
    358 	case VXSYSSLEEP:
    359 		secs = ARG1;
    360 		if(trace)
    361 			fprintf(stderr, "sleep %d\n", secs);
    362 		if (secs == 0)
    363 			ret = 0;
    364 		else
    365 			ret = sleep(secs);
    366 		break;
    367 
    368 	// Just to provide the classic "null system call" test...
    369 	case VXSYSGETPID:
    370 		fprintf(stderr, "getpid\n");
    371 		ret = progpid;
    372 		break;
    373 
    374 	default:
    375 		dumpregs(proc);
    376 		fatal("vxrun: bad system call %d", NUM);
    377 		ret = -1;
    378 	}
    379 	
    380 	if (ret < 0)
    381 		ret = -errno;
    382 	RET = ret;
    383 out:
    384 	//vxmem_unmap(proc->mem, m);	 XXX get rid of ref count?
    385 	;
    386 }
    387 
    388 static char**
    389 convertargs(vxproc *proc, char *base, uint32_t args)
    390 {
    391 	int n;
    392 	uint32_t a, s;
    393 	char **argv;
    394 
    395 	if (args == 0)
    396 		return NULL;
    397 	for (a=args;; a+=4) {
    398 		if (!vxmem_checkperm(proc->mem, a, 4, VXPERM_READ, NULL)){
    399 			if(trace)
    400 				fprintf(stderr, "bad args addr %p\n", V a);
    401 			return NULL;
    402 		}
    403 		s = *(uint32_t*)(base+a);
    404 		if (s == 0)
    405 			break;
    406 		if (!checkstring(proc->mem, base, s)){
    407 			if(trace)
    408 				fprintf(stderr, "bad arg string %p\n", V s);
    409 			return NULL;
    410 		}
    411 	}
    412 	n = (a - args) / 4 + 1;
    413 	argv = malloc(n*sizeof argv[0]);
    414 	if (argv == NULL)
    415 		return NULL;
    416 	n = 0;
    417 	for (a=args;; a+=4) {
    418 		s = *(uint32_t*)(base+a);
    419 		if (s == 0)
    420 			break;
    421 		argv[n++] = base+s;
    422 	}
    423 	argv[n] = NULL;
    424 	return argv;
    425 }
    426 
    427 static int
    428 doexec(vxproc *proc, char *base, uint32_t exe, uint32_t args, uint32_t envs)
    429 {
    430 	int i;
    431 
    432 	if(!checkstring(proc->mem, base, exe)){
    433 		if(trace)
    434 			fprintf(stderr, "exec [bad string %p]\n", V exe);
    435 	einval:
    436 		errno = EINVAL;
    437 		return -1;
    438 	}
    439 	if(trace)
    440 		fprintf(stderr, "exec %s\n", base+exe);
    441 	char **argv = convertargs(proc, base, args);
    442 	if(args && !argv)
    443 		goto einval;
    444 	char **envv = convertargs(proc, base, envs);
    445 	if(envs && !envv){
    446 		free(argv);
    447 		goto einval;
    448 	}
    449 	if(trace){
    450 		fprintf(stderr, "exec %s", base+exe);
    451 		if(argv)
    452 			for(i=0; argv[i]; i++)
    453 				fprintf(stderr, " %s", argv[i]);
    454 		fprintf(stderr, "\n");
    455 	}
    456 	execve(base+exe, argv, envv);
    457 	free(argv);
    458 	free(envv);
    459 	return -1;
    460 }
    461 
    462 extern char **environ;
    463 
    464 void runprog(const char *const *argv)
    465 {
    466 	vxproc *volatile p = vxproc_alloc();
    467 	if (p == NULL)
    468 		fatal("vxproc_new: %s\n", strerror(errno));
    469 	p->allowfp = 1;
    470 
    471 	const char *loadname = argv[0];
    472 	if (vxproc_loadelffile(p, loadname, &argv[0],
    473 			(const char**)environ) < 0)
    474 		fatal("vxproc_loadelffile: %s\n", strerror(errno));
    475 
    476 	// Come back here and return if the guest process calls exit(0)
    477 	// and we still have more repetitions to run.
    478 	if (setjmp(exitbuf)) {
    479 		vxproc_free(p);
    480 		return;
    481 	}
    482 
    483 	// Simple execution loop.
    484 	for (;;) {
    485 		int rc = vxproc_run(p);
    486 		if (rc < 0)
    487 			fatal("vxproc_run: %s\n", strerror(errno));
    488 		if (rc == VXTRAP_SYSCALL) {
    489 			dosyscall(p);
    490 			continue;
    491 		}
    492 		dumpregs(p);
    493 		fatal("vxproc_run trap %#x\n", rc);
    494 	}
    495 }
    496 
    497 int main(int argc, const char *const *argv)
    498 {
    499 	int i;
    500 	progname = argv[0];
    501 	progpid = getpid();
    502 	int reps = 1;
    503 
    504 	vx_elfbigmem = 1;
    505 
    506 	ARGBEGIN{
    507 	case 't':
    508 		trace++;
    509 		break;
    510 	case 'd':
    511 		vx32_debugxlate++;
    512 		break;
    513 	case 'v':
    514 		verbose++;
    515 		break;
    516 	case 'r':
    517 		runreps = atoi(ARGF());
    518 		if (runreps < 1) {
    519 			fprintf(stderr, "Invalid repeat count %d\n", runreps);
    520 			exit(1);
    521 		}
    522 		break;
    523 	default:
    524 	usage:
    525 		fprintf(stderr, "Usage: %s [-dtv] <vx-program> <args>\n",
    526 			progname);
    527 		exit(1);
    528 	}ARGEND
    529 	
    530 	if (argc < 1)
    531 		goto usage;
    532 
    533 	FILE *f = fopen("/dev/tty", "w");
    534 	if(f && verbose){
    535 		char buf[1000];
    536 		if(getcwd(buf, sizeof buf) != NULL)
    537 			fprintf(f, "cd %s\n", buf);
    538 		fprintf(f, "vxrun");
    539 		for(i=0; i<argc; i++)
    540 			fprintf(f, " %s", argv[i]);
    541 		fprintf(f, "\n");
    542 	}
    543 
    544 	vx32_siginit();
    545 
    546 	// Repeatedly load, execute, and unload the guest process
    547 	// as many times as requested.
    548 	do {
    549 		//fprintf(stderr, "run, reps=%d\n", runreps);
    550 		runprog(argv);
    551 	} while (--runreps > 0);
    552 
    553 	return 0;	// not reached
    554 }
    555