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