cc.c (13458B)
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, /* TODO: Remove Qflag */ 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 for (n = 0; n < cc1args.n; ++n) 226 addarg(tool, cc1args.s[n]); 227 for (n = 0; sysincludes[n]; ++n) { 228 addarg(tool, "-I"); 229 addarg(tool, sysincludes[n]); 230 } 231 fmt = "%s/libexec/scc/%s"; 232 goto local_tool; 233 case CC2: 234 fmt = cc2fmt(tool); 235 local_tool: 236 n = snprintf(t->cmd, sizeof(t->cmd), 237 fmt, prefix, t->bin, arch, abi); 238 if (n < 0 || n >= sizeof(t->cmd)) 239 die("scc-cc: target tool path is too long"); 240 break; 241 case QBE: 242 strcpy(t->cmd, "qbe"); 243 244 if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv")) 245 fmt = "amd64_sysv"; 246 else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv")) 247 fmt = "arm64"; 248 else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv")) 249 fmt = "rv64"; 250 else 251 die("not supported arch-abi (%s-%s) for qbe", arch, abi); 252 253 addarg(tool, "-t"); 254 addarg(tool, fmt); 255 break; 256 case LD: 257 if (!outfile) 258 outfile = "a.out"; 259 t->outfile = xstrdup(outfile); 260 if (gflag) 261 addarg(tool, "-g"); 262 if (sflag) 263 addarg(tool, "-s"); 264 for (n = 0; ldcmd[n]; n++) 265 addarg(tool, ldcmd[n]); 266 break; 267 case TEEIR: 268 if (Eflag && outfile) 269 t->outfile = xstrdup(outfile); 270 else 271 t->outfile = outfname(infile, "ir"); 272 addarg(tool, t->outfile); 273 break; 274 case TEEQBE: 275 t->outfile = outfname(infile, "qbe"); 276 addarg(tool, t->outfile); 277 break; 278 case TEEAS: 279 t->outfile = outfname(infile, "s"); 280 addarg(tool, t->outfile); 281 break; 282 case AS: 283 if (cflag && outfile) { 284 objfile = xstrdup(outfile); 285 } else { 286 objfile = (cflag || kflag) ? infile : NULL; 287 objfile = outfname(objfile, "o"); 288 } 289 t->outfile = xstrdup(objfile); 290 if (gflag) 291 addarg(tool, "-g"); 292 for (n = 0; ascmd[n]; n++) 293 addarg(tool, ascmd[n]); 294 break; 295 break; 296 default: 297 break; 298 } 299 300 if (fdin > -1) { 301 t->in = fdin; 302 fdin = -1; 303 } else { 304 t->in = -1; 305 if (infile) 306 addarg(tool, infile); 307 } 308 309 if (nexttool < LAST_TOOL) { 310 if (pipe(fds)) 311 die("scc-cc: pipe: %s", strerror(errno)); 312 t->out = fds[1]; 313 fdin = fds[0]; 314 } else { 315 t->out = -1; 316 } 317 318 addarg(tool, NULL); 319 320 return tool; 321 } 322 323 /* 324 * We cannot call exit() because it would call the atexit() 325 * handlers. If it finishes correctly we already called 326 * fflush() in the output buffers so it is not a problem 327 * not following the normal exit path. 328 */ 329 static void 330 tee(char *fname) 331 { 332 FILE *fp; 333 int ch; 334 335 if ((fp = fopen(fname, "w")) == NULL) 336 goto err; 337 338 while ((ch = getchar()) != EOF) { 339 putc(ch, stdout); 340 putc(ch, fp); 341 } 342 343 fflush(stdout); 344 fflush(fp); 345 346 if (ferror(stdin) || ferror(stdout) || ferror(fp)) 347 goto err; 348 _exit(0); 349 350 err: 351 fprintf(stderr, 352 "tee: teeing %s: %s\n", 353 fname, 354 strerror(errno)); 355 356 _exit(1); 357 } 358 359 static void 360 spawn(int tool) 361 { 362 int i; 363 char **ap; 364 struct tool *t = &tools[tool]; 365 366 switch (t->pid = fork()) { 367 case -1: 368 die("scc-cc: %s: %s", t->bin, strerror(errno)); 369 case 0: 370 if (t->out > -1) 371 dup2(t->out, 1); 372 if (t->in > -1) 373 dup2(t->in, 0); 374 375 if (tool == TEEAS && Sflag) 376 dup2(devnullfd, 1); 377 if (!dflag && tool != CC1 && tool != LD) 378 dup2(devnullfd, 2); 379 380 if (dflag) { 381 fprintf(stderr, "%s", t->cmd); 382 for (ap = t->args.s+1; *ap; ap++) 383 fprintf(stderr, " %s", *ap); 384 putc('\n', stderr); 385 } 386 if (strcmp(t->cmd, "tee") == 0) 387 tee(t->outfile); 388 execvp(t->cmd, t->args.s); 389 if (dflag) { 390 fprintf(stderr, 391 "scc-cc: execvp %s: %s\n", 392 t->cmd, 393 strerror(errno)); 394 } 395 abort(); 396 default: 397 if (t->in > -1) 398 close(t->in); 399 if (t->out > -1) 400 close(t->out); 401 402 for (i = 0; i < t->args.n; i++) 403 free(t->args.s[i]); 404 t->args.n = 0; 405 406 break; 407 } 408 } 409 410 static int 411 toolfor(char *file) 412 { 413 char *dot = strrchr(file, '.'); 414 415 if (Eflag) 416 return CC1; 417 418 if (dot) { 419 if (!strcmp(dot, ".c")) 420 return CC1; 421 if (!strcmp(dot, ".ir")) 422 return CC2; 423 if (!strcmp(dot, ".qbe")) 424 return QBE; 425 if (!strcmp(dot, ".s")) 426 return AS; 427 if (!strcmp(dot, ".o")) 428 return LD; 429 if (!strcmp(dot, ".a")) 430 return LD; 431 } else if (!strcmp(file, "-")) { 432 return CC1; 433 } 434 435 die("scc-cc: unrecognized filetype of %s", file); 436 } 437 438 static int 439 valid(int tool, struct tool *t) 440 { 441 int st; 442 443 if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st)) 444 goto internal; 445 if (WIFEXITED(st) && WEXITSTATUS(st) == 0) 446 return 1; 447 if (!failure && (tool == CC1 || tool == LD)) 448 goto fail; 449 450 internal: 451 if (!failure) 452 fprintf(stderr, "scc-cc:%s: internal error\n", t->bin); 453 fail: 454 failure = 1; 455 return 0; 456 } 457 458 static int 459 validatetools(void) 460 { 461 struct tool *t; 462 unsigned i; 463 int tool, st, failed = LAST_TOOL; 464 465 for (tool = 0; tool < LAST_TOOL; ++tool) { 466 t = &tools[tool]; 467 if (!t->pid) 468 continue; 469 if (!valid(tool, t)) 470 failed = tool; 471 if (tool >= failed && t->outfile) 472 unlink(t->outfile); 473 free(t->outfile); 474 t->pid = 0; 475 } 476 if (failed < LAST_TOOL) { 477 unlink(objfile); 478 return 0; 479 } 480 481 return 1; 482 } 483 484 static int 485 buildfile(char *file, int tool) 486 { 487 int nexttool; 488 489 for (; tool < LAST_TOOL; tool = nexttool) { 490 switch (tool) { 491 case CC1: 492 if (Eflag && outfile) 493 nexttool = TEEIR; 494 else if (Eflag || Mflag) 495 nexttool = LAST_TOOL; 496 else 497 nexttool = kflag ? TEEIR : CC2; 498 break; 499 case TEEIR: 500 nexttool = (Eflag) ? LAST_TOOL : CC2; 501 break; 502 case CC2: 503 if (Qflag) 504 nexttool = kflag ? TEEQBE : QBE; 505 else 506 nexttool = (Sflag || kflag) ? TEEAS : AS; 507 break; 508 case TEEQBE: 509 nexttool = QBE; 510 break; 511 case QBE: 512 nexttool = (Sflag || kflag) ? TEEAS : AS; 513 break; 514 case TEEAS: 515 nexttool = Sflag ? LAST_TOOL : AS; 516 break; 517 case AS: 518 nexttool = LAST_TOOL; 519 break; 520 default: 521 nexttool = LAST_TOOL; 522 continue; 523 } 524 525 spawn(settool(tool, file, nexttool)); 526 } 527 528 return validatetools(); 529 } 530 531 static void 532 build(struct items *chain, int link) 533 { 534 int i, tool; 535 536 for (i = 0; i < chain->n; ++i) { 537 if (!strcmp(chain->s[i], "-l")) { 538 newitem(&linkargs, chain->s[i++]); 539 newitem(&linkargs, chain->s[i]); 540 continue; 541 } 542 tool = toolfor(chain->s[i]); 543 if (tool == LD) { 544 newitem(&linkargs, chain->s[i]); 545 continue; 546 } 547 if (buildfile(chain->s[i], tool)) { 548 newitem(&linkargs, objfile); 549 newitem((!link || kflag) ? &objout : &objtmp, objfile); 550 } 551 } 552 } 553 554 static void 555 usage(void) 556 { 557 fputs("usage: scc-cc [options] file...\n", stderr); 558 exit(1); 559 } 560 561 static char * 562 param(char **argp, char ***argv) 563 { 564 char *s; 565 566 if ((*argp)[1] != '\0') { 567 s = (*argp) + 1; 568 *argp += strlen(*argp) - 1; 569 } else if ((*argv)[1] != NULL) { 570 s = (*argv)[1]; 571 (*argv)++; 572 } else { 573 usage(); 574 } 575 576 return s; 577 } 578 579 int 580 main(int argc, char *argv[]) 581 { 582 struct items linkchain = { .n = 0, }; 583 int link, n; 584 585 atexit(terminate); 586 signal(SIGHUP, sighandler); 587 signal(SIGINT, sighandler); 588 signal(SIGTERM, sighandler); 589 590 if (!(arch = getenv("ARCH"))) 591 arch = ARCH; 592 if (!(sys = getenv("SYS"))) 593 sys = SYS; 594 if (!(abi = getenv("ABI"))) 595 abi = ABI; 596 if (!(format = getenv("FORMAT"))) 597 format = FORMAT; 598 if (!(prefix = getenv("SCCPREFIX"))) 599 prefix = PREFIX; 600 if (!(libprefix = getenv("SCCLIBPREFIX"))) 601 libprefix = LIBPREFIX; 602 603 for (++argv; *argv; ++argv) { 604 char *arg = *argv; 605 if (arg[0] == '-' && arg[1] == '-') 606 break; 607 if (arg[0] != '-') { 608 newitem(&linkchain, arg); 609 continue; 610 } 611 612 if (!strcmp(arg, "-static") || 613 !strcmp(arg, "-dynamic") || 614 !strcmp(arg, "-pedantic") || 615 !strcmp(arg, "-ansi") || 616 !strncmp(arg, "-std=", 5)) { 617 continue; 618 } 619 620 for (++arg; *arg; ++arg) { 621 switch (*arg) { 622 case 'D': 623 newitem(&cc1args, "-D"); 624 newitem(&cc1args, param(&arg, &argv)); 625 break; 626 case 'M': 627 Mflag = 1; 628 break; 629 case 'E': 630 Eflag = 1; 631 break; 632 case 'I': 633 newitem(&cc1args, "-I"); 634 newitem(&cc1args, param(&arg, &argv)); 635 break; 636 case 'L': 637 newitem(&linkargs, "-L"); 638 newitem(&linkargs, param(&arg, &argv)); 639 break; 640 case 'O': 641 param(&arg, &argv); 642 break; 643 case 'S': 644 Sflag = 1; 645 break; 646 case 'U': 647 newitem(&cc1args, "-U"); 648 newitem(&cc1args, param(&arg, &argv)); 649 break; 650 case 'c': 651 cflag = 1; 652 break; 653 case 'd': 654 dflag = 1; 655 break; 656 case 'g': 657 gflag = 1; 658 break; 659 case 'k': 660 kflag = 1; 661 break; 662 case 'l': 663 newitem(&linkchain, "-l"); 664 newitem(&linkchain, param(&arg, &argv)); 665 break; 666 case 'a': 667 arch = param(&arg, &argv); 668 break; 669 case 't': 670 sys = param(&arg, &argv); 671 break; 672 case 'o': 673 outfile = param(&arg, &argv); 674 break; 675 case 's': 676 sflag = 1; 677 break; 678 case 'w': 679 Wflag = 1; 680 break; 681 case 'W': 682 param(&arg, &argv); 683 Wflag = 1; 684 break; 685 case 'q': 686 Qflag = 0; 687 break; 688 case 'Q': 689 Qflag = 1; 690 break; 691 case 'm': 692 case 'f': 693 /* ignore any -f or -m option */ 694 param(&arg, &argv); 695 break; 696 default: 697 usage(); 698 } 699 } 700 } 701 702 for (; *argv; ++argv) 703 newitem(&linkchain, *argv); 704 705 if (Eflag && linkchain.n == 0) 706 newitem(&linkchain, "-"); 707 708 if (Eflag && Mflag || 709 (Eflag || Mflag) && (Sflag || kflag) || 710 linkchain.n == 0 || 711 linkchain.n > 1 && cflag && outfile) 712 usage(); 713 714 if (!dflag) { 715 if ((devnullfd = open("/dev/null", O_WRONLY)) < 0) 716 fputs("scc-cc: could not open /dev/null\n", stderr); 717 } 718 719 if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0]) 720 tmpdir = "."; 721 tmpdirln = strlen(tmpdir); 722 723 build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag))); 724 725 if (!(link || cflag)) 726 return failure; 727 728 if (link && !failure) { 729 spawn(settool(LD, NULL, LAST_TOOL)); 730 validatetools(); 731 } 732 733 return failure; 734 }