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