1 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 * 27 * Copyright 2018 Richard Lowe. 28 * Copyright 2019 Joyent, Inc. 29 */ 30 31 /* 32 * Wrapper for the GNU C compiler to make it accept the Sun C compiler 33 * arguments where possible. 34 * 35 * Since the translation is inexact, this is something of a work-in-progress. 36 * 37 */ 38 39 /* 40 * If you modify this file, you must increment CW_VERSION. This is a semver, 41 * incompatible changes should bump the major, anything else the minor. 42 */ 43 #define CW_VERSION "9.0" 44 45 /* 46 * -# Verbose mode 47 * -### Show compiler commands built by driver, no compilation 48 * -A<name[(tokens)]> Preprocessor predicate assertion 49 * -C Prevent preprocessor from removing comments 50 * -c Compile only - produce .o files, suppress linking 51 * -D<name[=token]> Associate name with token as if by #define 52 * -d[y|n] dynamic [-dy] or static [-dn] option to linker 53 * -E Compile source through preprocessor only, output to stdout 54 * -erroff=<t> Suppress warnings specified by tags t(%none, %all, <tag list>) 55 * -errtags=<a> Display messages with tags a(no, yes) 56 * -errwarn=<t> Treats warnings specified by tags t(%none, %all, <tag list>) 57 * as errors 58 * -g Compile for debugging 59 * -H Print path name of each file included during compilation 60 * -h <name> Assign <name> to generated dynamic shared library 61 * -I<dir> Add <dir> to preprocessor #include file search path 62 * -i Passed to linker to ignore any LD_LIBRARY_PATH setting 63 * -keeptmp Keep temporary files created during compilation 64 * -L<dir> Pass to linker to add <dir> to the library search path 65 * -l<name> Link with library lib<name>.a or lib<name>.so 66 * -mt Specify options needed when compiling multi-threaded code 67 * -O Use default optimization level (-xO2 or -xO3. Check man page.) 68 * -o <outputfile> Set name of output file to <outputfile> 69 * -P Compile source through preprocessor only, output to .i file 70 * -p Compile for profiling with prof 71 * -R<dir[:dir]> Build runtime search path list into executable 72 * -S Compile and only generate assembly code (.s) 73 * -s Strip symbol table from the executable file 74 * -t Turn off duplicate symbol warnings when linking 75 * -U<name> Delete initial definition of preprocessor symbol <name> 76 * -V Report version number of each compilation phase 77 * -v Do stricter semantic checking 78 * -W<c>,<arg> Pass <arg> to specified component <c> (a,l,m,p,0,2,h,i,u) 79 * -w Suppress compiler warning messages 80 * -Xa Compile assuming ANSI C conformance, allow K & R extensions 81 * (default mode) 82 * -Xs Compile assuming (pre-ANSI) K & R C style code 83 * -xarch=<a> Specify target architecture instruction set 84 * -xbuiltin[=<b>] When profitable inline, or substitute intrinisic functions 85 * for system functions, b={%all,%none} 86 * -xchip=<c> Specify the target processor for use by the optimizer 87 * -xO<n> Generate optimized code (n={1|2|3|4|5}) 88 * -xtarget=<t> Specify target system for optimization 89 * -YI,<dir> Specify <dir> for location of headers 90 */ 91 92 /* 93 * Translation table: 94 */ 95 /* 96 * -# -v 97 * -### error 98 * -A<name[(tokens)]> pass-thru 99 * -B<[static|dynamic]> pass-thru (syntax error for anything else) 100 * -C pass-thru 101 * -c pass-thru 102 * -D<name[=token]> pass-thru 103 * -E pass-thru 104 * -errtags=%all -Wall 105 * -errwarn=%all -Werror else -Wno-error 106 * -g pass-thru 107 * -H pass-thru 108 * -I<dir> pass-thru 109 * -i pass-thru 110 * -keeptmp -save-temps 111 * -L<dir> pass-thru 112 * -l<name> pass-thru 113 * -mt -D_REENTRANT 114 * -nolib -nodefaultlibs 115 * -O -O1 (Check the man page to be certain) 116 * -o <outputfile> pass-thru 117 * -P -E -o filename.i (or error) 118 * -p pass-thru 119 * -R<dir[:dir]> pass-thru 120 * -S pass-thru 121 * -U<name> pass-thru 122 * -V --version 123 * -v -Wall 124 * -Wa,<arg> pass-thru 125 * -Wp,<arg> pass-thru 126 * -Wl,<arg> pass-thru 127 * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone 128 * -Wu,-save_args -msave-args 129 * -w pass-thru 130 * -Xa -std=iso9899:199409 or -ansi 131 * -Xs -traditional -std=c89 132 * -xarch=<a> table 133 * -xbuiltin[=<b>] -fbuiltin (-fno-builtin otherwise) 134 * -xchip=<c> table 135 * -xO<n> -O<n> 136 * -xtarget=<t> table 137 * -xtransition -Wtransition 138 * -YI,<dir> -nostdinc -I<dir> 139 */ 140 141 #include <ctype.h> 142 #include <err.h> 143 #include <errno.h> 144 #include <fcntl.h> 145 #include <getopt.h> 146 #include <stdio.h> 147 #include <stdlib.h> 148 #include <stdbool.h> 149 #include <string.h> 150 #include <unistd.h> 151 #include <dirent.h> 152 153 #include <sys/param.h> 154 #include <sys/stat.h> 155 #include <sys/types.h> 156 #include <sys/utsname.h> 157 #include <sys/wait.h> 158 159 #define CW_F_CXX 0x01 160 #define CW_F_SHADOW 0x02 161 #define CW_F_EXEC 0x04 162 #define CW_F_ECHO 0x08 163 #define CW_F_XLATE 0x10 164 #define CW_F_PROG 0x20 165 166 typedef enum cw_op { 167 CW_O_NONE = 0, 168 CW_O_PREPROCESS, 169 CW_O_COMPILE, 170 CW_O_LINK 171 } cw_op_t; 172 173 struct aelist { 174 struct ae { 175 struct ae *ae_next; 176 char *ae_arg; 177 } *ael_head, *ael_tail; 178 int ael_argc; 179 }; 180 181 typedef enum { 182 GNU, 183 SUN, 184 SMATCH 185 } compiler_style_t; 186 187 typedef struct { 188 char *c_name; 189 char *c_path; 190 compiler_style_t c_style; 191 } cw_compiler_t; 192 193 typedef struct cw_ictx { 194 struct cw_ictx *i_next; 195 cw_compiler_t *i_compiler; 196 char *i_linker; 197 struct aelist *i_ae; 198 uint32_t i_flags; 199 int i_oldargc; 200 char **i_oldargv; 201 pid_t i_pid; 202 char *i_tmpdir; 203 char *i_stderr; 204 } cw_ictx_t; 205 206 /* 207 * Status values to indicate which Studio compiler and associated 208 * flags are being used. 209 */ 210 #define M32 0x01 /* -m32 - only on Studio 12 */ 211 #define M64 0x02 /* -m64 - only on Studio 12 */ 212 #define SS11 0x100 /* Studio 11 */ 213 #define SS12 0x200 /* Studio 12 */ 214 215 #define TRANS_ENTRY 5 216 /* 217 * Translation table definition for the -xarch= flag. The "x_arg" 218 * value is translated into the appropriate gcc flags according 219 * to the values in x_trans[n]. The x_flags indicates what compiler 220 * is being used and what flags have been set via the use of 221 * "x_arg". 222 */ 223 typedef struct xarch_table { 224 char *x_arg; 225 int x_flags; 226 char *x_trans[TRANS_ENTRY]; 227 } xarch_table_t; 228 229 /* 230 * The translation table for the -xarch= flag used in the Studio compilers. 231 */ 232 static const xarch_table_t xtbl[] = { 233 #if defined(__x86) 234 { "generic", SS11, {NULL} }, 235 { "generic64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 236 { "amd64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 237 { "386", SS11, { "-march=i386" } }, 238 { "pentium_pro", SS11, { "-march=pentiumpro" } }, 239 { "sse", SS11, { "-msse", "-mfpmath=sse" } }, 240 { "sse2", SS11, { "-msse2", "-mfpmath=sse" } }, 241 #endif 242 }; 243 244 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t); 245 246 static const char *xchip_tbl[] = { 247 #if defined(__x86) 248 "386", "-mtune=i386", NULL, 249 "486", "-mtune=i486", NULL, 250 "pentium", "-mtune=pentium", NULL, 251 "pentium_pro", "-mtune=pentiumpro", NULL, 252 #endif 253 NULL, NULL 254 }; 255 256 static const char *xtarget_tbl[] = { 257 #if defined(__x86) 258 "pentium_pro", "-march=pentiumpro", NULL, 259 #endif /* __x86 */ 260 NULL, NULL 261 }; 262 263 static void 264 nomem(void) 265 { 266 errx(1, "out of memory"); 267 } 268 269 static void 270 newae(struct aelist *ael, const char *arg) 271 { 272 struct ae *ae; 273 274 if ((ae = calloc(1, sizeof (*ae))) == NULL) 275 nomem(); 276 ae->ae_arg = strdup(arg); 277 if (ael->ael_tail == NULL) 278 ael->ael_head = ae; 279 else 280 ael->ael_tail->ae_next = ae; 281 ael->ael_tail = ae; 282 ael->ael_argc++; 283 } 284 285 static cw_ictx_t * 286 newictx(void) 287 { 288 cw_ictx_t *ctx = calloc(1, sizeof (cw_ictx_t)); 289 if (ctx) 290 if ((ctx->i_ae = calloc(1, sizeof (struct aelist))) == NULL) { 291 free(ctx); 292 return (NULL); 293 } 294 295 return (ctx); 296 } 297 298 static void 299 error(const char *arg) 300 { 301 errx(2, "error: mapping failed at or near arg '%s'", arg); 302 } 303 304 /* 305 * Add the current favourite set of warnings to the gcc invocation. 306 */ 307 static void 308 warnings(struct aelist *h) 309 { 310 static int warningsonce; 311 312 if (warningsonce++) 313 return; 314 315 /* 316 * Enable as many warnings as exist, then disable those that we never 317 * ever want. 318 */ 319 newae(h, "-Wall"); 320 newae(h, "-Wextra"); 321 } 322 323 static void 324 optim_disable(struct aelist *h, int level) 325 { 326 if (level >= 2) { 327 newae(h, "-fno-strict-aliasing"); 328 newae(h, "-fno-unit-at-a-time"); 329 newae(h, "-fno-optimize-sibling-calls"); 330 } 331 } 332 333 static void 334 Xsmode(struct aelist *h) 335 { 336 static int xsonce; 337 338 if (xsonce++) 339 return; 340 341 newae(h, "-traditional"); 342 newae(h, "-traditional-cpp"); 343 } 344 345 static void 346 usage(void) 347 { 348 extern char *__progname; 349 (void) fprintf(stderr, 350 "usage: %s [-C] [--versions] --primary <compiler> " 351 "[--shadow <compiler>]... -- cflags...\n", 352 __progname); 353 (void) fprintf(stderr, "compilers take the form: name,path,style\n" 354 " - name: a unique name usable in flag specifiers\n" 355 " - path: path to the compiler binary\n" 356 " - style: the style of flags expected: either sun or gnu\n"); 357 exit(2); 358 } 359 360 static int 361 xlate_xtb(struct aelist *h, const char *xarg) 362 { 363 int i, j; 364 365 for (i = 0; i < xtbl_size; i++) { 366 if (strcmp(xtbl[i].x_arg, xarg) == 0) 367 break; 368 } 369 370 /* 371 * At the end of the table and so no matching "arg" entry 372 * found and so this must be a bad -xarch= flag. 373 */ 374 if (i == xtbl_size) 375 error(xarg); 376 377 for (j = 0; j < TRANS_ENTRY; j++) { 378 if (xtbl[i].x_trans[j] != NULL) 379 newae(h, xtbl[i].x_trans[j]); 380 else 381 break; 382 } 383 return (xtbl[i].x_flags); 384 385 } 386 387 static void 388 xlate(struct aelist *h, const char *xarg, const char **table) 389 { 390 while (*table != NULL && strcmp(xarg, *table) != 0) { 391 while (*table != NULL) 392 table++; 393 table++; 394 } 395 396 if (*table == NULL) 397 error(xarg); 398 399 table++; 400 401 while (*table != NULL) { 402 newae(h, *table); 403 table++; 404 } 405 } 406 407 /* 408 * The compiler wants the output file to end in appropriate extension. If 409 * we're generating a name from whole cloth (path == NULL), we assume that 410 * extension to be .o, otherwise we match the extension of the caller. 411 */ 412 static char * 413 discard_file_name(cw_ictx_t *ctx, const char *path) 414 { 415 char *ret, *ext; 416 char tmpl[] = "cwXXXXXX"; 417 418 if (path == NULL) { 419 ext = ".o"; 420 } else { 421 ext = strrchr(path, '.'); 422 } 423 424 /* 425 * We need absolute control over where the temporary file goes, since 426 * we rely on it for cleanup so tempnam(3C) and tmpnam(3C) are 427 * inappropriate (they use TMPDIR, preferentially). 428 * 429 * mkstemp(3C) doesn't actually help us, since the temporary file 430 * isn't used by us, only its name. 431 */ 432 if (mktemp(tmpl) == NULL) 433 nomem(); 434 435 (void) asprintf(&ret, "%s/%s%s", ctx->i_tmpdir, tmpl, 436 (ext != NULL) ? ext : ""); 437 438 if (ret == NULL) 439 nomem(); 440 441 return (ret); 442 } 443 444 static bool 445 is_source_file(const char *path) 446 { 447 char *ext = strrchr(path, '.'); 448 449 if ((ext == NULL) || (*(ext + 1) == '\0')) 450 return (false); 451 452 ext += 1; 453 454 if ((strcasecmp(ext, "c") == 0) || 455 (strcmp(ext, "cc") == 0) || 456 (strcmp(ext, "i") == 0) || 457 (strcasecmp(ext, "s") == 0) || 458 (strcmp(ext, "cpp") == 0)) { 459 return (true); 460 } 461 462 return (false); 463 } 464 465 static bool 466 is_asm_file(const char *path) 467 { 468 char *ext = strrchr(path, '.'); 469 470 if ((ext == NULL) || (*(ext + 1) == '\0')) 471 return (false); 472 473 ext += 1; 474 475 if (strcasecmp(ext, "s") == 0) 476 return (true); 477 478 return (false); 479 } 480 481 static void 482 do_gcc(cw_ictx_t *ctx) 483 { 484 int c; 485 int nolibc = 0; 486 int in_output = 0, seen_o = 0, c_files = 0; 487 cw_op_t op = CW_O_LINK; 488 char *model = NULL; 489 char *nameflag; 490 int mflag = 0; 491 492 if (ctx->i_flags & CW_F_PROG) { 493 newae(ctx->i_ae, "--version"); 494 return; 495 } 496 497 newae(ctx->i_ae, "-fident"); 498 newae(ctx->i_ae, "-finline"); 499 newae(ctx->i_ae, "-fno-inline-functions"); 500 newae(ctx->i_ae, "-fno-builtin"); 501 newae(ctx->i_ae, "-fno-asm"); 502 newae(ctx->i_ae, "-fdiagnostics-show-option"); 503 newae(ctx->i_ae, "-nodefaultlibs"); 504 505 /* 506 * This is needed because 'u' is defined 507 * under a conditional on 'sun'. Should 508 * probably just remove the conditional, 509 * or make it be dependent on '__sun'. 510 * 511 * -Dunix is also missing in enhanced ANSI mode 512 */ 513 newae(ctx->i_ae, "-D__sun"); 514 515 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 516 nomem(); 517 518 /* 519 * Walk the argument list, translating as we go .. 520 */ 521 while (--ctx->i_oldargc > 0) { 522 char *arg = *++ctx->i_oldargv; 523 size_t arglen = strlen(arg); 524 525 if (*arg == '-') { 526 arglen--; 527 } else { 528 if (!in_output && is_source_file(arg)) 529 c_files++; 530 531 /* 532 * Otherwise, filenames and partial arguments 533 * are passed through for gcc to chew on. However, 534 * output is always discarded for the secondary 535 * compiler. 536 */ 537 if ((ctx->i_flags & CW_F_SHADOW) && in_output) { 538 newae(ctx->i_ae, discard_file_name(ctx, arg)); 539 } else { 540 newae(ctx->i_ae, arg); 541 } 542 in_output = 0; 543 continue; 544 } 545 546 if (ctx->i_flags & CW_F_CXX) { 547 if (strncmp(arg, "-_g++=", 6) == 0) { 548 newae(ctx->i_ae, strchr(arg, '=') + 1); 549 continue; 550 } 551 552 if (strcmp(arg, "-xwe") == 0) { 553 /* turn warnings into errors */ 554 newae(ctx->i_ae, "-Werror"); 555 continue; 556 } 557 if (strcmp(arg, "-nolib") == 0) { 558 /* -nodefaultlibs is on by default */ 559 nolibc = 1; 560 continue; 561 } 562 } 563 564 switch ((c = arg[1])) { 565 case '_': 566 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 567 (strncmp(arg, "-_gcc=", 6) == 0) || 568 (strncmp(arg, "-_gnu=", 6) == 0)) { 569 newae(ctx->i_ae, strchr(arg, '=') + 1); 570 } 571 break; 572 case '#': 573 if (arglen == 1) { 574 newae(ctx->i_ae, "-v"); 575 break; 576 } 577 error(arg); 578 break; 579 case 'f': 580 if ((strcmp(arg, "-fpic") == 0) || 581 (strcmp(arg, "-fPIC") == 0)) { 582 newae(ctx->i_ae, arg); 583 break; 584 } 585 error(arg); 586 break; 587 case 'E': 588 if (arglen == 1) { 589 newae(ctx->i_ae, "-xc"); 590 newae(ctx->i_ae, arg); 591 op = CW_O_PREPROCESS; 592 nolibc = 1; 593 break; 594 } 595 error(arg); 596 break; 597 case 'c': 598 case 'S': 599 if (arglen == 1) { 600 op = CW_O_COMPILE; 601 nolibc = 1; 602 } 603 /* FALLTHROUGH */ 604 case 'C': 605 case 'H': 606 case 'p': 607 if (arglen == 1) { 608 newae(ctx->i_ae, arg); 609 break; 610 } 611 error(arg); 612 break; 613 case 'A': 614 case 'g': 615 case 'I': 616 case 'i': 617 case 'L': 618 case 'l': 619 case 'R': 620 case 'U': 621 case 'u': 622 case 'w': 623 newae(ctx->i_ae, arg); 624 break; 625 case 'o': 626 seen_o = 1; 627 if (arglen == 1) { 628 in_output = 1; 629 newae(ctx->i_ae, arg); 630 } else if (ctx->i_flags & CW_F_SHADOW) { 631 newae(ctx->i_ae, "-o"); 632 newae(ctx->i_ae, discard_file_name(ctx, arg)); 633 } else { 634 newae(ctx->i_ae, arg); 635 } 636 break; 637 case 'D': 638 newae(ctx->i_ae, arg); 639 /* 640 * XXX Clearly a hack ... do we need _KADB too? 641 */ 642 if (strcmp(arg, "-D_KERNEL") == 0 || 643 strcmp(arg, "-D_BOOT") == 0) 644 newae(ctx->i_ae, "-ffreestanding"); 645 break; 646 case 'e': 647 if (strcmp(arg, "-errtags=yes") == 0) { 648 warnings(ctx->i_ae); 649 break; 650 } 651 if (strcmp(arg, "-errwarn=%all") == 0) { 652 newae(ctx->i_ae, "-Werror"); 653 break; 654 } 655 error(arg); 656 break; 657 case 'k': 658 if (strcmp(arg, "-keeptmp") == 0) { 659 newae(ctx->i_ae, "-save-temps"); 660 break; 661 } 662 error(arg); 663 break; 664 case 'm': 665 if (strcmp(arg, "-mt") == 0) { 666 newae(ctx->i_ae, "-D_REENTRANT"); 667 break; 668 } 669 if (strcmp(arg, "-m64") == 0) { 670 newae(ctx->i_ae, "-m64"); 671 #if defined(__x86) 672 newae(ctx->i_ae, "-mtune=opteron"); 673 #endif 674 mflag |= M64; 675 break; 676 } 677 if (strcmp(arg, "-m32") == 0) { 678 newae(ctx->i_ae, "-m32"); 679 mflag |= M32; 680 break; 681 } 682 error(arg); 683 break; 684 case 'O': 685 if (arglen == 1) { 686 newae(ctx->i_ae, "-O"); 687 break; 688 } 689 error(arg); 690 break; 691 case 'P': 692 /* 693 * We could do '-E -o filename.i', but that's hard, 694 * and we don't need it for the case that's triggering 695 * this addition. We'll require the user to specify 696 * -o in the Makefile. If they don't they'll find out 697 * in a hurry. 698 */ 699 newae(ctx->i_ae, "-E"); 700 op = CW_O_PREPROCESS; 701 nolibc = 1; 702 break; 703 case 's': 704 if (strcmp(arg, "-shared") == 0) { 705 newae(ctx->i_ae, "-shared"); 706 nolibc = 1; 707 break; 708 } 709 error(arg); 710 break; 711 712 case 'V': 713 if (arglen == 1) { 714 ctx->i_flags &= ~CW_F_ECHO; 715 newae(ctx->i_ae, "--version"); 716 break; 717 } 718 error(arg); 719 break; 720 case 'v': 721 if (arglen == 1) { 722 warnings(ctx->i_ae); 723 break; 724 } 725 error(arg); 726 break; 727 case 'W': 728 if (strncmp(arg, "-Wa,", 4) == 0 || 729 strncmp(arg, "-Wp,", 4) == 0 || 730 strncmp(arg, "-Wl,", 4) == 0) { 731 newae(ctx->i_ae, arg); 732 break; 733 } 734 735 #if defined(__x86) 736 if (strcmp(arg, "-Wu,-save_args") == 0) { 737 newae(ctx->i_ae, "-msave-args"); 738 break; 739 } 740 #endif /* __x86 */ 741 error(arg); 742 break; 743 case 'X': 744 if (strcmp(arg, "-Xs") == 0) { 745 Xsmode(ctx->i_ae); 746 break; 747 } 748 error(arg); 749 break; 750 case 'x': 751 if (arglen == 1) 752 error(arg); 753 switch (arg[2]) { 754 case 'a': 755 if (strncmp(arg, "-xarch=", 7) == 0) { 756 mflag |= xlate_xtb(ctx->i_ae, arg + 7); 757 break; 758 } 759 error(arg); 760 break; 761 case 'b': 762 if (strncmp(arg, "-xbuiltin=", 10) == 0) { 763 if (strcmp(arg + 10, "%all")) 764 newae(ctx->i_ae, "-fbuiltin"); 765 break; 766 } 767 error(arg); 768 break; 769 case 'c': 770 if (strncmp(arg, "-xc99=%all", 10) == 0) { 771 newae(ctx->i_ae, "-std=gnu99"); 772 break; 773 } 774 if (strncmp(arg, "-xc99=%none", 11) == 0) { 775 newae(ctx->i_ae, "-std=gnu89"); 776 break; 777 } 778 if (strncmp(arg, "-xchip=", 7) == 0) { 779 xlate(ctx->i_ae, arg + 7, xchip_tbl); 780 break; 781 } 782 783 error(arg); 784 break; 785 #if defined(__x86) 786 case 'm': 787 if (strcmp(arg, "-xmodel=kernel") == 0) { 788 newae(ctx->i_ae, "-ffreestanding"); 789 newae(ctx->i_ae, "-mno-red-zone"); 790 model = "-mcmodel=kernel"; 791 nolibc = 1; 792 break; 793 } 794 error(arg); 795 break; 796 #endif /* __x86 */ 797 case 'O': 798 if (strncmp(arg, "-xO", 3) == 0) { 799 size_t len = strlen(arg); 800 char *s = NULL; 801 int c = *(arg + 3); 802 int level; 803 804 if (len != 4 || !isdigit(c)) 805 error(arg); 806 807 level = atoi(arg + 3); 808 if (level > 5) 809 error(arg); 810 if (level >= 2) { 811 /* 812 * For gcc-3.4.x at -O2 we 813 * need to disable optimizations 814 * that break ON. 815 */ 816 optim_disable(ctx->i_ae, level); 817 /* 818 * limit -xO3 to -O2 as well. 819 */ 820 level = 2; 821 } 822 if (asprintf(&s, "-O%d", level) == -1) 823 nomem(); 824 newae(ctx->i_ae, s); 825 free(s); 826 break; 827 } 828 error(arg); 829 break; 830 case 't': 831 if (strncmp(arg, "-xtarget=", 9) == 0) { 832 xlate(ctx->i_ae, arg + 9, xtarget_tbl); 833 break; 834 } 835 error(arg); 836 break; 837 default: 838 error(arg); 839 break; 840 } 841 break; 842 case 'Y': 843 if (arglen == 1) { 844 if ((arg = *++ctx->i_oldargv) == NULL || 845 *arg == '\0') 846 error("-Y"); 847 ctx->i_oldargc--; 848 arglen = strlen(arg + 1); 849 } else { 850 arg += 2; 851 } 852 if (strncmp(arg, "I,", 2) == 0) { 853 char *s = strdup(arg); 854 s[0] = '-'; 855 s[1] = 'I'; 856 newae(ctx->i_ae, "-nostdinc"); 857 newae(ctx->i_ae, s); 858 free(s); 859 break; 860 } 861 error(arg); 862 break; 863 default: 864 error(arg); 865 break; 866 } 867 } 868 869 free(nameflag); 870 871 /* 872 * When compiling multiple source files in a single invocation some 873 * compilers output objects into the current directory with 874 * predictable and conventional names. 875 * 876 * We prevent any attempt to compile multiple files at once so that 877 * any such objects created by a shadow can't escape into a later 878 * link-edit. 879 */ 880 if (c_files > 1 && op != CW_O_PREPROCESS) { 881 errx(2, "multiple source files are " 882 "allowed only with -E or -P"); 883 } 884 885 /* 886 * Make sure that we do not have any unintended interactions between 887 * the xarch options passed in and the version of the Studio compiler 888 * used. 889 */ 890 if ((mflag & (SS11|SS12)) == (SS11|SS12)) { 891 errx(2, 892 "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n"); 893 } 894 895 switch (mflag) { 896 case 0: 897 case M32: 898 case M64: 899 case SS12: 900 case SS11: 901 case (SS11|M32): 902 case (SS11|M64): 903 case (SS12|M32): 904 case (SS12|M64): 905 break; 906 default: 907 (void) fprintf(stderr, 908 "Incompatible -xarch= and/or -m32/-m64 options used.\n"); 909 exit(2); 910 } 911 912 if (ctx->i_flags & CW_F_SHADOW) { 913 if (op == CW_O_PREPROCESS) 914 exit(0); 915 else if (op == CW_O_LINK && c_files == 0) 916 exit(0); 917 } 918 919 if (model != NULL) 920 newae(ctx->i_ae, model); 921 if (!nolibc) 922 newae(ctx->i_ae, "-lc"); 923 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 924 newae(ctx->i_ae, "-o"); 925 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 926 } 927 } 928 929 static void 930 do_smatch(cw_ictx_t *ctx) 931 { 932 if (ctx->i_flags & CW_F_PROG) { 933 newae(ctx->i_ae, "--version"); 934 return; 935 } 936 937 /* 938 * Some sources shouldn't run smatch at all. 939 */ 940 for (int i = 0; i < ctx->i_oldargc; i++) { 941 char *arg = ctx->i_oldargv[i]; 942 943 if (strcmp(arg, "-_smatch=off") == 0) { 944 ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO); 945 return; 946 } 947 948 /* smatch can't handle asm */ 949 if ((arg[0] != '-') && is_asm_file(arg)) { 950 ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO); 951 return; 952 } 953 } 954 955 /* 956 * smatch can handle gcc's options. 957 */ 958 do_gcc(ctx); 959 } 960 961 static void 962 do_cc(cw_ictx_t *ctx) 963 { 964 int in_output = 0, seen_o = 0, c_files = 0; 965 cw_op_t op = CW_O_LINK; 966 char *nameflag; 967 968 if (ctx->i_flags & CW_F_PROG) { 969 newae(ctx->i_ae, "-V"); 970 return; 971 } 972 973 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 974 nomem(); 975 976 while (--ctx->i_oldargc > 0) { 977 char *arg = *++ctx->i_oldargv; 978 979 if (strncmp(arg, "-_CC=", 5) == 0) { 980 newae(ctx->i_ae, strchr(arg, '=') + 1); 981 continue; 982 } 983 984 if (*arg != '-') { 985 if (!in_output && is_source_file(arg)) 986 c_files++; 987 988 if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) { 989 newae(ctx->i_ae, arg); 990 } else { 991 in_output = 0; 992 newae(ctx->i_ae, discard_file_name(ctx, arg)); 993 } 994 continue; 995 } 996 switch (*(arg + 1)) { 997 case '_': 998 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 999 (strncmp(arg, "-_cc=", 5) == 0) || 1000 (strncmp(arg, "-_sun=", 6) == 0)) { 1001 newae(ctx->i_ae, strchr(arg, '=') + 1); 1002 } 1003 break; 1004 1005 case 'V': 1006 ctx->i_flags &= ~CW_F_ECHO; 1007 newae(ctx->i_ae, arg); 1008 break; 1009 case 'o': 1010 seen_o = 1; 1011 if (strlen(arg) == 2) { 1012 in_output = 1; 1013 newae(ctx->i_ae, arg); 1014 } else if (ctx->i_flags & CW_F_SHADOW) { 1015 newae(ctx->i_ae, "-o"); 1016 newae(ctx->i_ae, discard_file_name(ctx, arg)); 1017 } else { 1018 newae(ctx->i_ae, arg); 1019 } 1020 break; 1021 case 'c': 1022 case 'S': 1023 if (strlen(arg) == 2) 1024 op = CW_O_COMPILE; 1025 newae(ctx->i_ae, arg); 1026 break; 1027 case 'E': 1028 case 'P': 1029 if (strlen(arg) == 2) 1030 op = CW_O_PREPROCESS; 1031 /*FALLTHROUGH*/ 1032 default: 1033 newae(ctx->i_ae, arg); 1034 } 1035 } 1036 1037 free(nameflag); 1038 1039 /* See the comment on this same code in do_gcc() */ 1040 if (c_files > 1 && op != CW_O_PREPROCESS) { 1041 errx(2, "multiple source files are " 1042 "allowed only with -E or -P"); 1043 } 1044 1045 if (ctx->i_flags & CW_F_SHADOW) { 1046 if (op == CW_O_PREPROCESS) 1047 exit(0); 1048 else if (op == CW_O_LINK && c_files == 0) 1049 exit(0); 1050 } 1051 1052 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 1053 newae(ctx->i_ae, "-o"); 1054 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 1055 } 1056 } 1057 1058 static void 1059 prepctx(cw_ictx_t *ctx) 1060 { 1061 newae(ctx->i_ae, ctx->i_compiler->c_path); 1062 1063 if (ctx->i_flags & CW_F_PROG) { 1064 (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ? 1065 "shadow" : "primary", ctx->i_compiler->c_path); 1066 (void) fflush(stdout); 1067 } 1068 1069 /* 1070 * If LD_ALTEXEC is already set, the expectation would be that that 1071 * link-editor is run, as such we need to leave it the environment 1072 * alone and let that happen. 1073 */ 1074 if ((ctx->i_linker != NULL) && (getenv("LD_ALTEXEC") == NULL)) 1075 setenv("LD_ALTEXEC", ctx->i_linker, 1); 1076 1077 if (!(ctx->i_flags & CW_F_XLATE)) 1078 return; 1079 1080 switch (ctx->i_compiler->c_style) { 1081 case SUN: 1082 do_cc(ctx); 1083 break; 1084 case GNU: 1085 do_gcc(ctx); 1086 break; 1087 case SMATCH: 1088 do_smatch(ctx); 1089 break; 1090 } 1091 } 1092 1093 static int 1094 invoke(cw_ictx_t *ctx) 1095 { 1096 char **newargv, *makeflags; 1097 int ac; 1098 struct ae *a; 1099 1100 newargv = calloc(ctx->i_ae->ael_argc + 1, sizeof (*newargv)); 1101 if (newargv == NULL) 1102 nomem(); 1103 1104 /* 1105 * Check to see if the silent make flag is present (-s), if so, do not 1106 * echo. The MAKEFLAGS environment variable is set by dmake. By 1107 * observation it appears to place short flags without any arguments 1108 * first followed by any long form flags or flags with arguments. 1109 */ 1110 makeflags = getenv("MAKEFLAGS"); 1111 if (makeflags != NULL) { 1112 size_t makeflags_len = strlen(makeflags); 1113 for (size_t i = 0; i < makeflags_len; i++) { 1114 if (makeflags[i] == 's') { 1115 ctx->i_flags &= ~CW_F_ECHO; 1116 break; 1117 } 1118 /* end of short flags */ 1119 if (makeflags[i] == ' ') 1120 break; 1121 } 1122 } 1123 1124 if (ctx->i_flags & CW_F_ECHO) 1125 (void) fprintf(stderr, "+ "); 1126 1127 for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) { 1128 newargv[ac] = a->ae_arg; 1129 if (ctx->i_flags & CW_F_ECHO) 1130 (void) fprintf(stderr, "%s ", a->ae_arg); 1131 if (a == ctx->i_ae->ael_tail) 1132 break; 1133 } 1134 1135 if (ctx->i_flags & CW_F_ECHO) { 1136 (void) fprintf(stderr, "\n"); 1137 (void) fflush(stderr); 1138 } 1139 1140 if (!(ctx->i_flags & CW_F_EXEC)) 1141 return (0); 1142 1143 /* 1144 * We must fix up the environment here so that the dependency files are 1145 * not trampled by the shadow compiler. Also take care of GCC 1146 * environment variables that will throw off gcc. This assumes a primary 1147 * gcc. 1148 */ 1149 if ((ctx->i_flags & CW_F_SHADOW) && 1150 (unsetenv("SUNPRO_DEPENDENCIES") != 0 || 1151 unsetenv("DEPENDENCIES_OUTPUT") != 0 || 1152 unsetenv("GCC_ROOT") != 0)) { 1153 (void) fprintf(stderr, "error: environment setup failed: %s\n", 1154 strerror(errno)); 1155 return (-1); 1156 } 1157 1158 (void) execv(newargv[0], newargv); 1159 warn("couldn't run %s", newargv[0]); 1160 1161 return (-1); 1162 } 1163 1164 static int 1165 reap(cw_ictx_t *ctx) 1166 { 1167 int status, ret = 0; 1168 char buf[1024]; 1169 struct stat s; 1170 1171 /* 1172 * Only wait for one specific child. 1173 */ 1174 if (ctx->i_pid <= 0) 1175 return (-1); 1176 1177 do { 1178 if (waitpid(ctx->i_pid, &status, 0) < 0) { 1179 warn("cannot reap child"); 1180 return (-1); 1181 } 1182 if (status != 0) { 1183 if (WIFSIGNALED(status)) { 1184 ret = -WTERMSIG(status); 1185 break; 1186 } else if (WIFEXITED(status)) { 1187 ret = WEXITSTATUS(status); 1188 break; 1189 } 1190 } 1191 } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 1192 1193 if (stat(ctx->i_stderr, &s) < 0) { 1194 warn("stat failed on child cleanup"); 1195 return (-1); 1196 } 1197 if (s.st_size != 0) { 1198 FILE *f; 1199 1200 if ((f = fopen(ctx->i_stderr, "r")) != NULL) { 1201 while (fgets(buf, sizeof (buf), f)) 1202 (void) fprintf(stderr, "%s", buf); 1203 (void) fflush(stderr); 1204 (void) fclose(f); 1205 } 1206 } 1207 (void) unlink(ctx->i_stderr); 1208 free(ctx->i_stderr); 1209 1210 /* 1211 * cc returns an error code when given -V; we want that to succeed. 1212 */ 1213 if (ctx->i_flags & CW_F_PROG) 1214 return (0); 1215 1216 return (ret); 1217 } 1218 1219 static int 1220 exec_ctx(cw_ictx_t *ctx, int block) 1221 { 1222 if ((ctx->i_stderr = tempnam(ctx->i_tmpdir, "cw")) == NULL) { 1223 nomem(); 1224 return (-1); 1225 } 1226 1227 if ((ctx->i_pid = fork()) == 0) { 1228 int fd; 1229 1230 (void) fclose(stderr); 1231 if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL, 1232 0666)) < 0) { 1233 err(1, "open failed for standard error"); 1234 } 1235 if (dup2(fd, 2) < 0) { 1236 err(1, "dup2 failed for standard error"); 1237 } 1238 if (fd != 2) 1239 (void) close(fd); 1240 if (freopen("/dev/fd/2", "w", stderr) == NULL) { 1241 err(1, "freopen failed for /dev/fd/2"); 1242 } 1243 1244 prepctx(ctx); 1245 exit(invoke(ctx)); 1246 } 1247 1248 if (ctx->i_pid < 0) { 1249 err(1, "fork failed"); 1250 } 1251 1252 if (block) 1253 return (reap(ctx)); 1254 1255 return (0); 1256 } 1257 1258 static void 1259 parse_compiler(const char *spec, cw_compiler_t *compiler) 1260 { 1261 char *tspec, *token; 1262 1263 if ((tspec = strdup(spec)) == NULL) 1264 nomem(); 1265 1266 if ((token = strsep(&tspec, ",")) == NULL) 1267 errx(1, "Compiler is missing a name: %s", spec); 1268 compiler->c_name = token; 1269 1270 if ((token = strsep(&tspec, ",")) == NULL) 1271 errx(1, "Compiler is missing a path: %s", spec); 1272 compiler->c_path = token; 1273 1274 if ((token = strsep(&tspec, ",")) == NULL) 1275 errx(1, "Compiler is missing a style: %s", spec); 1276 1277 if ((strcasecmp(token, "gnu") == 0) || 1278 (strcasecmp(token, "gcc") == 0)) { 1279 compiler->c_style = GNU; 1280 } else if ((strcasecmp(token, "sun") == 0) || 1281 (strcasecmp(token, "cc") == 0)) { 1282 compiler->c_style = SUN; 1283 } else if ((strcasecmp(token, "smatch") == 0)) { 1284 compiler->c_style = SMATCH; 1285 } else { 1286 errx(1, "unknown compiler style: %s", token); 1287 } 1288 1289 if (tspec != NULL) 1290 errx(1, "Excess tokens in compiler: %s", spec); 1291 } 1292 1293 static void 1294 cleanup(cw_ictx_t *ctx) 1295 { 1296 DIR *dirp; 1297 struct dirent *dp; 1298 char buf[MAXPATHLEN]; 1299 1300 if ((dirp = opendir(ctx->i_tmpdir)) == NULL) { 1301 if (errno != ENOENT) { 1302 err(1, "couldn't open temp directory: %s", 1303 ctx->i_tmpdir); 1304 } else { 1305 return; 1306 } 1307 } 1308 1309 errno = 0; 1310 while ((dp = readdir(dirp)) != NULL) { 1311 (void) snprintf(buf, MAXPATHLEN, "%s/%s", ctx->i_tmpdir, 1312 dp->d_name); 1313 1314 if (strcmp(dp->d_name, ".") == 0 || 1315 strcmp(dp->d_name, "..") == 0) { 1316 continue; 1317 } 1318 1319 if (unlink(buf) == -1) 1320 err(1, "failed to unlink temp file: %s", dp->d_name); 1321 errno = 0; 1322 } 1323 1324 if (errno != 0) { 1325 err(1, "failed to read temporary directory: %s", 1326 ctx->i_tmpdir); 1327 } 1328 1329 (void) closedir(dirp); 1330 if (rmdir(ctx->i_tmpdir) != 0) { 1331 err(1, "failed to unlink temporary directory: %s", 1332 ctx->i_tmpdir); 1333 } 1334 } 1335 1336 int 1337 main(int argc, char **argv) 1338 { 1339 int ch; 1340 cw_compiler_t primary = { NULL, NULL, 0 }; 1341 cw_compiler_t shadows[10]; 1342 int nshadows = 0; 1343 int ret = 0; 1344 bool do_serial; 1345 bool do_exec; 1346 bool vflg = false; 1347 bool Cflg = false; 1348 bool cflg = false; 1349 bool nflg = false; 1350 char *tmpdir; 1351 1352 cw_ictx_t *main_ctx; 1353 1354 static struct option longopts[] = { 1355 { "compiler", no_argument, NULL, 'c' }, 1356 { "linker", required_argument, NULL, 'l' }, 1357 { "noecho", no_argument, NULL, 'n' }, 1358 { "primary", required_argument, NULL, 'p' }, 1359 { "shadow", required_argument, NULL, 's' }, 1360 { "versions", no_argument, NULL, 'v' }, 1361 { NULL, 0, NULL, 0 }, 1362 }; 1363 1364 1365 if ((main_ctx = newictx()) == NULL) 1366 nomem(); 1367 1368 while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) { 1369 switch (ch) { 1370 case 'c': 1371 cflg = true; 1372 break; 1373 case 'C': 1374 Cflg = true; 1375 break; 1376 case 'l': 1377 if ((main_ctx->i_linker = strdup(optarg)) == NULL) 1378 nomem(); 1379 break; 1380 case 'n': 1381 nflg = true; 1382 break; 1383 case 'p': 1384 if (primary.c_path != NULL) { 1385 warnx("Only one primary compiler may " 1386 "be specified"); 1387 usage(); 1388 } 1389 1390 parse_compiler(optarg, &primary); 1391 break; 1392 case 's': 1393 if (nshadows >= 10) 1394 errx(1, "May only use 10 shadows at " 1395 "the moment"); 1396 parse_compiler(optarg, &shadows[nshadows]); 1397 nshadows++; 1398 break; 1399 case 'v': 1400 vflg = true; 1401 break; 1402 default: 1403 (void) fprintf(stderr, "Did you forget '--'?\n"); 1404 usage(); 1405 } 1406 } 1407 1408 if (primary.c_path == NULL) { 1409 warnx("A primary compiler must be specified"); 1410 usage(); 1411 } 1412 1413 do_serial = getenv("CW_SHADOW_SERIAL") != NULL; 1414 do_exec = getenv("CW_NO_EXEC") == NULL; 1415 1416 /* Leave room for argv[0] */ 1417 argc -= (optind - 1); 1418 argv += (optind - 1); 1419 1420 main_ctx->i_oldargc = argc; 1421 main_ctx->i_oldargv = argv; 1422 main_ctx->i_flags = CW_F_XLATE; 1423 if (nflg == 0) 1424 main_ctx->i_flags |= CW_F_ECHO; 1425 if (do_exec) 1426 main_ctx->i_flags |= CW_F_EXEC; 1427 if (Cflg) 1428 main_ctx->i_flags |= CW_F_CXX; 1429 main_ctx->i_compiler = &primary; 1430 1431 if (cflg) { 1432 (void) fputs(primary.c_path, stdout); 1433 } 1434 1435 if (vflg) { 1436 (void) printf("cw version %s\n", CW_VERSION); 1437 (void) fflush(stdout); 1438 main_ctx->i_flags &= ~CW_F_ECHO; 1439 main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC; 1440 do_serial = 1; 1441 } 1442 1443 tmpdir = getenv("TMPDIR"); 1444 if (tmpdir == NULL) 1445 tmpdir = "/tmp"; 1446 1447 if (asprintf(&main_ctx->i_tmpdir, "%s/cw.XXXXXX", tmpdir) == -1) 1448 nomem(); 1449 1450 if ((main_ctx->i_tmpdir = mkdtemp(main_ctx->i_tmpdir)) == NULL) 1451 errx(1, "failed to create temporary directory"); 1452 1453 ret |= exec_ctx(main_ctx, do_serial); 1454 1455 for (int i = 0; i < nshadows; i++) { 1456 int r; 1457 cw_ictx_t *shadow_ctx; 1458 1459 if ((shadow_ctx = newictx()) == NULL) 1460 nomem(); 1461 1462 (void) memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t)); 1463 1464 shadow_ctx->i_flags |= CW_F_SHADOW; 1465 shadow_ctx->i_compiler = &shadows[i]; 1466 1467 r = exec_ctx(shadow_ctx, do_serial); 1468 if (r == 0) { 1469 shadow_ctx->i_next = main_ctx->i_next; 1470 main_ctx->i_next = shadow_ctx; 1471 } 1472 ret |= r; 1473 } 1474 1475 if (!do_serial) { 1476 cw_ictx_t *next = main_ctx; 1477 while (next != NULL) { 1478 cw_ictx_t *toreap = next; 1479 next = next->i_next; 1480 ret |= reap(toreap); 1481 } 1482 } 1483 1484 cleanup(main_ctx); 1485 return (ret); 1486 } 1487