cc.c (13477B)
1 /* 2 * Scc is a pure C99 source code, except this file that is 3 * intended for a POSIX.2008 environment. This situation creates 4 * a problem where some systems require the macro _ANSI_SOURCE to 5 * limit the namespace to a pure ISO namespace. NetBSD had a bug 6 * and using POSIX_C_SOURCE and _ANSI_SOURCE at the same time 7 * generates a syntax error in the system headers. The bug was 8 * already fixed in NetBSD but keeping the workaround here does 9 * not hurt and it helps to keep back compatibility. 10 */ 11 #undef _POSIX_C_SOURCE 12 #undef _ANSI_SOURCE 13 #define _POSIX_C_SOURCE 200809L 14 15 #include <fcntl.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 #include <unistd.h> 19 20 #include <assert.h> 21 #include <errno.h> 22 #include <limits.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include <scc/config.h> 29 #include <scc/scc.h> 30 #include <scc/sys.h> 31 32 enum { 33 CC1, 34 TEEIR, 35 CC2, 36 TEEQBE, 37 QBE, 38 TEEAS, 39 AS, 40 LD, 41 LAST_TOOL, 42 }; 43 44 static struct tool { 45 char cmd[PATH_MAX]; 46 char bin[32]; 47 char *outfile; 48 struct items args; 49 int in, out; 50 pid_t pid; 51 } tools[] = { 52 [CC1] = {.bin = "cc1"}, 53 [TEEIR] = {.bin = "tee"}, 54 [CC2] = {.bin = "cc2"}, 55 [TEEQBE] = {.bin = "tee"}, 56 [QBE] = {.bin = "qbe"}, 57 [TEEAS] = {.bin = "tee"}, 58 [AS] = {.bin = ASBIN}, 59 [LD] = {.bin = LDBIN}, 60 }; 61 62 static char *arch, *sys, *abi, *format; 63 static char *objfile, *outfile; 64 static char *prefix, *libprefix; 65 static char *tmpdir; 66 static size_t tmpdirln; 67 static struct items objtmp, objout; 68 static struct items linkargs, cc1args; 69 70 static int Mflag, Eflag, Sflag, Wflag, 71 cflag, dflag, kflag, sflag, Qflag = 1, 72 gflag; 73 74 static int devnullfd = -1; 75 76 extern int failure; 77 78 static void 79 terminate(void) 80 { 81 unsigned i; 82 83 if (!kflag) { 84 for (i = 0; i < objtmp.n; ++i) 85 unlink(objtmp.s[i]); 86 } 87 } 88 89 static void 90 sighandler(int sig) 91 { 92 terminate(); 93 _exit(1); 94 } 95 96 static void 97 addarg(int tool, char *arg) 98 { 99 struct tool *t = &tools[tool]; 100 char *p, buff[FILENAME_MAX]; 101 size_t len, cnt; 102 int n; 103 104 if (!arg) { 105 newitem(&t->args, NULL); 106 return; 107 } 108 109 if (arg[0] == '-') { 110 newitem(&t->args, xstrdup(arg)); 111 return; 112 } 113 114 if (!strcmp(arg, "%c")) { 115 for (n = 0; n < linkargs.n; ++n) 116 newitem(&t->args, xstrdup(linkargs.s[n])); 117 return; 118 } 119 120 for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) { 121 if (*arg != '%') { 122 buff[cnt++] = *arg; 123 continue; 124 } 125 126 switch (*++arg) { 127 case 'a': 128 p = arch; 129 break; 130 case 's': 131 p = sys; 132 break; 133 case 'p': 134 p = libprefix; 135 break; 136 case 'b': 137 p = abi; 138 break; 139 case 'o': 140 p = t->outfile; 141 break; 142 default: 143 buff[cnt++] = *arg; 144 continue; 145 } 146 147 len = strlen(p); 148 if (len + cnt >= FILENAME_MAX) 149 die("scc-cc: pathname too long"); 150 memcpy(buff+cnt, p, len); 151 cnt += len; 152 } 153 154 if (cnt >= FILENAME_MAX) 155 abort(); 156 157 buff[cnt] = '\0'; 158 newitem(&t->args, xstrdup(buff)); 159 } 160 161 static char * 162 cc2fmt(int tool) 163 { 164 if (Qflag) 165 return "%s/libexec/scc/%s-qbe_%s-%s"; 166 return "%s/libexec/scc/%s-%s-%s"; 167 } 168 169 static char * 170 outfname(char *path, char *type) 171 { 172 char *new, sep, *p; 173 size_t newsz, pathln; 174 int tmpfd, n; 175 176 if (path) { 177 sep = '.'; 178 if (p = strrchr(path, '/')) 179 path = p + 1; 180 pathln = strlen(path); 181 if (p = strrchr(path, '.')) 182 pathln -= strlen(p); 183 } else { 184 sep = '/'; 185 type = "scc-XXXXXX"; 186 path = tmpdir; 187 pathln = tmpdirln; 188 } 189 190 newsz = pathln + 1 + strlen(type) + 1; 191 new = xmalloc(newsz); 192 n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type); 193 if (n < 0 || n >= newsz) 194 die("scc-cc: wrong output filename"); 195 if (sep == '/') { 196 if ((tmpfd = mkstemp(new)) < 0) 197 die("scc-cc: could not create output file '%s': %s", 198 new, strerror(errno)); 199 close(tmpfd); 200 } 201 202 return new; 203 } 204 205 static int 206 settool(int tool, char *infile, int nexttool) 207 { 208 struct tool *t = &tools[tool]; 209 char *fmt; 210 int n, fds[2]; 211 static int fdin = -1; 212 213 strcpy(t->cmd, t->bin); 214 assert(t->args.n == 0); 215 newitem(&t->args, xstrdup(t->bin)); 216 217 switch (tool) { 218 case CC1: 219 if (Eflag) 220 addarg(tool, "-E"); 221 if (Mflag) 222 addarg(tool, "-M"); 223 if (Wflag) 224 addarg(tool, "-w"); 225 if (Qflag) 226 addarg(tool, "-D__QBE__"); 227 228 for (n = 0; n < cc1args.n; ++n) 229 addarg(tool, cc1args.s[n]); 230 for (n = 0; sysincludes[n]; ++n) { 231 addarg(tool, "-I"); 232 addarg(tool, sysincludes[n]); 233 } 234 fmt = "%s/libexec/scc/%s"; 235 goto local_tool; 236 case CC2: 237 fmt = cc2fmt(tool); 238 local_tool: 239 n = snprintf(t->cmd, sizeof(t->cmd), 240 fmt, prefix, t->bin, arch, abi); 241 if (n < 0 || n >= sizeof(t->cmd)) 242 die("scc-cc: target tool path is too long"); 243 break; 244 case QBE: 245 strcpy(t->cmd, "qbe"); 246 247 if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv")) 248 fmt = "amd64_sysv"; 249 else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv")) 250 fmt = "arm64"; 251 else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv")) 252 fmt = "rv64"; 253 else 254 die("not supported arch-abi (%s-%s) for qbe", arch, abi); 255 256 addarg(tool, "-t"); 257 addarg(tool, fmt); 258 break; 259 case LD: 260 if (!outfile) 261 outfile = "a.out"; 262 t->outfile = xstrdup(outfile); 263 if (gflag) 264 addarg(tool, "-g"); 265 if (sflag) 266 addarg(tool, "-s"); 267 for (n = 0; ldcmd[n]; n++) 268 addarg(tool, ldcmd[n]); 269 break; 270 case TEEIR: 271 if (Eflag && outfile) 272 t->outfile = xstrdup(outfile); 273 else 274 t->outfile = outfname(infile, "ir"); 275 addarg(tool, t->outfile); 276 break; 277 case TEEQBE: 278 t->outfile = outfname(infile, "qbe"); 279 addarg(tool, t->outfile); 280 break; 281 case TEEAS: 282 t->outfile = outfname(infile, "s"); 283 addarg(tool, t->outfile); 284 break; 285 case AS: 286 if (cflag && outfile) { 287 objfile = xstrdup(outfile); 288 } else { 289 objfile = (cflag || kflag) ? infile : NULL; 290 objfile = outfname(objfile, "o"); 291 } 292 t->outfile = xstrdup(objfile); 293 if (gflag) 294 addarg(tool, "-g"); 295 for (n = 0; ascmd[n]; n++) 296 addarg(tool, ascmd[n]); 297 break; 298 break; 299 default: 300 break; 301 } 302 303 if (fdin > -1) { 304 t->in = fdin; 305 fdin = -1; 306 } else { 307 t->in = -1; 308 if (infile) 309 addarg(tool, infile); 310 } 311 312 if (nexttool < LAST_TOOL) { 313 if (pipe(fds)) 314 die("scc-cc: pipe: %s", strerror(errno)); 315 t->out = fds[1]; 316 fdin = fds[0]; 317 } else { 318 t->out = -1; 319 } 320 321 addarg(tool, NULL); 322 323 return tool; 324 } 325 326 /* 327 * We cannot call exit() because it would call the atexit() 328 * handlers. If it finishes correctly we already called 329 * fflush() in the output buffers so it is not a problem 330 * not following the normal exit path. 331 */ 332 static void 333 tee(char *fname) 334 { 335 FILE *fp; 336 int ch; 337 338 if ((fp = fopen(fname, "w")) == NULL) 339 goto err; 340 341 while ((ch = getchar()) != EOF) { 342 putc(ch, stdout); 343 putc(ch, fp); 344 } 345 346 fflush(stdout); 347 fflush(fp); 348 349 if (ferror(stdin) || ferror(stdout) || ferror(fp)) 350 goto err; 351 _exit(0); 352 353 err: 354 fprintf(stderr, 355 "tee: teeing %s: %s\n", 356 fname, 357 strerror(errno)); 358 359 _exit(1); 360 } 361 362 static void 363 spawn(int tool) 364 { 365 int i; 366 char **ap; 367 struct tool *t = &tools[tool]; 368 369 switch (t->pid = fork()) { 370 case -1: 371 die("scc-cc: %s: %s", t->bin, strerror(errno)); 372 case 0: 373 if (t->out > -1) 374 dup2(t->out, 1); 375 if (t->in > -1) 376 dup2(t->in, 0); 377 378 if (tool == TEEAS && Sflag) 379 dup2(devnullfd, 1); 380 if (!dflag && tool != CC1 && tool != LD) 381 dup2(devnullfd, 2); 382 383 if (dflag) { 384 fprintf(stderr, "%s", t->cmd); 385 for (ap = t->args.s+1; *ap; ap++) 386 fprintf(stderr, " %s", *ap); 387 putc('\n', stderr); 388 } 389 if (strcmp(t->cmd, "tee") == 0) 390 tee(t->outfile); 391 execvp(t->cmd, t->args.s); 392 if (dflag) { 393 fprintf(stderr, 394 "scc-cc: execvp %s: %s\n", 395 t->cmd, 396 strerror(errno)); 397 } 398 abort(); 399 default: 400 if (t->in > -1) 401 close(t->in); 402 if (t->out > -1) 403 close(t->out); 404 405 for (i = 0; i < t->args.n; i++) 406 free(t->args.s[i]); 407 t->args.n = 0; 408 409 break; 410 } 411 } 412 413 static int 414 toolfor(char *file) 415 { 416 char *dot = strrchr(file, '.'); 417 418 if (Eflag) 419 return CC1; 420 421 if (dot) { 422 if (!strcmp(dot, ".c")) 423 return CC1; 424 if (!strcmp(dot, ".ir")) 425 return CC2; 426 if (!strcmp(dot, ".qbe")) 427 return QBE; 428 if (!strcmp(dot, ".s")) 429 return AS; 430 if (!strcmp(dot, ".o")) 431 return LD; 432 if (!strcmp(dot, ".a")) 433 return LD; 434 } else if (!strcmp(file, "-")) { 435 return CC1; 436 } 437 438 die("scc-cc: unrecognized filetype of %s", file); 439 } 440 441 static int 442 valid(int tool, struct tool *t) 443 { 444 int st; 445 446 if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st)) 447 goto internal; 448 if (WIFEXITED(st) && WEXITSTATUS(st) == 0) 449 return 1; 450 if (!failure && (tool == CC1 || tool == LD)) 451 goto fail; 452 453 internal: 454 if (!failure) 455 fprintf(stderr, "scc-cc:%s: internal error\n", t->bin); 456 fail: 457 failure = 1; 458 return 0; 459 } 460 461 static int 462 validatetools(void) 463 { 464 struct tool *t; 465 unsigned i; 466 int tool, st, failed = LAST_TOOL; 467 468 for (tool = 0; tool < LAST_TOOL; ++tool) { 469 t = &tools[tool]; 470 if (!t->pid) 471 continue; 472 if (!valid(tool, t)) 473 failed = tool; 474 if (tool >= failed && t->outfile) 475 unlink(t->outfile); 476 free(t->outfile); 477 t->pid = 0; 478 } 479 if (failed < LAST_TOOL) { 480 unlink(objfile); 481 return 0; 482 } 483 484 return 1; 485 } 486 487 static int 488 buildfile(char *file, int tool) 489 { 490 int nexttool; 491 492 for (; tool < LAST_TOOL; tool = nexttool) { 493 switch (tool) { 494 case CC1: 495 if (Eflag && outfile) 496 nexttool = TEEIR; 497 else if (Eflag || Mflag) 498 nexttool = LAST_TOOL; 499 else 500 nexttool = kflag ? TEEIR : CC2; 501 break; 502 case TEEIR: 503 nexttool = (Eflag) ? LAST_TOOL : CC2; 504 break; 505 case CC2: 506 if (Qflag) 507 nexttool = kflag ? TEEQBE : QBE; 508 else 509 nexttool = (Sflag || kflag) ? TEEAS : AS; 510 break; 511 case TEEQBE: 512 nexttool = QBE; 513 break; 514 case QBE: 515 nexttool = (Sflag || kflag) ? TEEAS : AS; 516 break; 517 case TEEAS: 518 nexttool = Sflag ? LAST_TOOL : AS; 519 break; 520 case AS: 521 nexttool = LAST_TOOL; 522 break; 523 default: 524 nexttool = LAST_TOOL; 525 continue; 526 } 527 528 spawn(settool(tool, file, nexttool)); 529 } 530 531 return validatetools(); 532 } 533 534 static void 535 build(struct items *chain, int link) 536 { 537 int i, tool; 538 539 for (i = 0; i < chain->n; ++i) { 540 if (!strcmp(chain->s[i], "-l")) { 541 newitem(&linkargs, chain->s[i++]); 542 newitem(&linkargs, chain->s[i]); 543 continue; 544 } 545 tool = toolfor(chain->s[i]); 546 if (tool == LD) { 547 newitem(&linkargs, chain->s[i]); 548 continue; 549 } 550 if (buildfile(chain->s[i], tool)) { 551 newitem(&linkargs, objfile); 552 newitem((!link || kflag) ? &objout : &objtmp, objfile); 553 } 554 } 555 } 556 557 static void 558 usage(void) 559 { 560 fputs("usage: scc-cc [options] file...\n", stderr); 561 exit(1); 562 } 563 564 static char * 565 param(char **argp, char ***argv) 566 { 567 char *s; 568 569 if ((*argp)[1] != '\0') { 570 s = (*argp) + 1; 571 *argp += strlen(*argp) - 1; 572 } else if ((*argv)[1] != NULL) { 573 s = (*argv)[1]; 574 (*argv)++; 575 } else { 576 usage(); 577 } 578 579 return s; 580 } 581 582 int 583 main(int argc, char *argv[]) 584 { 585 struct items linkchain = { .n = 0, }; 586 int link, n; 587 588 atexit(terminate); 589 signal(SIGHUP, sighandler); 590 signal(SIGINT, sighandler); 591 signal(SIGTERM, sighandler); 592 593 if (!(arch = getenv("ARCH"))) 594 arch = ARCH; 595 if (!(sys = getenv("SYS"))) 596 sys = SYS; 597 if (!(abi = getenv("ABI"))) 598 abi = ABI; 599 if (!(format = getenv("FORMAT"))) 600 format = FORMAT; 601 if (!(prefix = getenv("SCCPREFIX"))) 602 prefix = PREFIX; 603 if (!(libprefix = getenv("SCCLIBPREFIX"))) 604 libprefix = LIBPREFIX; 605 606 for (++argv; *argv; ++argv) { 607 char *arg = *argv; 608 if (arg[0] == '-' && arg[1] == '-') 609 break; 610 if (arg[0] != '-') { 611 newitem(&linkchain, arg); 612 continue; 613 } 614 615 if (!strcmp(arg, "-static") || 616 !strcmp(arg, "-dynamic") || 617 !strcmp(arg, "-pedantic") || 618 !strcmp(arg, "-ansi") || 619 !strncmp(arg, "-std=", 5)) { 620 continue; 621 } 622 623 for (++arg; *arg; ++arg) { 624 switch (*arg) { 625 case 'D': 626 newitem(&cc1args, "-D"); 627 newitem(&cc1args, param(&arg, &argv)); 628 break; 629 case 'M': 630 Mflag = 1; 631 break; 632 case 'E': 633 Eflag = 1; 634 break; 635 case 'I': 636 newitem(&cc1args, "-I"); 637 newitem(&cc1args, param(&arg, &argv)); 638 break; 639 case 'L': 640 newitem(&linkargs, "-L"); 641 newitem(&linkargs, param(&arg, &argv)); 642 break; 643 case 'O': 644 param(&arg, &argv); 645 break; 646 case 'S': 647 Sflag = 1; 648 break; 649 case 'U': 650 newitem(&cc1args, "-U"); 651 newitem(&cc1args, param(&arg, &argv)); 652 break; 653 case 'c': 654 cflag = 1; 655 break; 656 case 'd': 657 dflag = 1; 658 break; 659 case 'g': 660 gflag = 1; 661 break; 662 case 'k': 663 kflag = 1; 664 break; 665 case 'l': 666 newitem(&linkchain, "-l"); 667 newitem(&linkchain, param(&arg, &argv)); 668 break; 669 case 'a': 670 arch = param(&arg, &argv); 671 break; 672 case 't': 673 sys = param(&arg, &argv); 674 break; 675 case 'o': 676 outfile = param(&arg, &argv); 677 break; 678 case 's': 679 sflag = 1; 680 break; 681 case 'w': 682 Wflag = 1; 683 break; 684 case 'W': 685 param(&arg, &argv); 686 Wflag = 1; 687 break; 688 case 'q': 689 Qflag = 0; 690 break; 691 case 'Q': 692 Qflag = 1; 693 break; 694 case 'm': 695 case 'f': 696 /* ignore any -f or -m option */ 697 param(&arg, &argv); 698 break; 699 default: 700 usage(); 701 } 702 } 703 } 704 705 for (; *argv; ++argv) 706 newitem(&linkchain, *argv); 707 708 if (Eflag && linkchain.n == 0) 709 newitem(&linkchain, "-"); 710 711 if (Eflag && Mflag || 712 (Eflag || Mflag) && (Sflag || kflag) || 713 linkchain.n == 0 || 714 linkchain.n > 1 && cflag && outfile) 715 usage(); 716 717 if (!dflag) { 718 if ((devnullfd = open("/dev/null", O_WRONLY)) < 0) 719 fputs("scc-cc: could not open /dev/null\n", stderr); 720 } 721 722 if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0]) 723 tmpdir = "."; 724 tmpdirln = strlen(tmpdir); 725 726 build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag))); 727 728 if (!(link || cflag)) 729 return failure; 730 731 if (link && !failure) { 732 spawn(settool(LD, NULL, LAST_TOOL)); 733 validatetools(); 734 } 735 736 return failure; 737 }