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 2018, Richard Lowe. 25 */ 26 /* 27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 * 30 * Copyright 2019 Joyent, Inc. 31 */ 32 33 /* 34 * Wrapper for the GNU C compiler to make it accept the Sun C compiler 35 * arguments where possible. 36 * 37 * Since the translation is inexact, this is something of a work-in-progress. 38 * 39 */ 40 41 /* If you modify this file, you must increment CW_VERSION */ 42 #define CW_VERSION "5.1" 43 44 /* 45 * -# Verbose mode 46 * -### Show compiler commands built by driver, no compilation 47 * -A<name[(tokens)]> Preprocessor predicate assertion 48 * -C Prevent preprocessor from removing comments 49 * -c Compile only - produce .o files, suppress linking 50 * -cg92 Alias for -xtarget=ss1000 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 * -fast Optimize using a selection of options 59 * -fd Report old-style function definitions and declarations 60 * -fnonstd Initialize floating-point hardware to non-standard preferences 61 * -fns[=<yes|no>] Select non-standard floating point mode 62 * -fprecision=<p> Set FP rounding precision mode p(single, double, extended) 63 * -fround=<r> Select the IEEE rounding mode in effect at startup 64 * -fsimple[=<n>] Select floating-point optimization preferences <n> 65 * -fsingle Use single-precision arithmetic (-Xt and -Xs modes only) 66 * -ftrap=<t> Select floating-point trapping mode in effect at startup 67 * -fstore force floating pt. values to target precision on assignment 68 * -g Compile for debugging 69 * -H Print path name of each file included during compilation 70 * -h <name> Assign <name> to generated dynamic shared library 71 * -I<dir> Add <dir> to preprocessor #include file search path 72 * -i Passed to linker to ignore any LD_LIBRARY_PATH setting 73 * -keeptmp Keep temporary files created during compilation 74 * -L<dir> Pass to linker to add <dir> to the library search path 75 * -l<name> Link with library lib<name>.a or lib<name>.so 76 * -mc Remove duplicate strings from .comment section of output files 77 * -mr Remove all strings from .comment section of output files 78 * -mr,"string" Remove all strings and append "string" to .comment section 79 * -mt Specify options needed when compiling multi-threaded code 80 * -native Find available processor, generate code accordingly 81 * -nofstore Do not force floating pt. values to target precision 82 * on assignment 83 * -norunpath Do not build in a runtime path for shared libraries 84 * -O Use default optimization level (-xO2 or -xO3. Check man page.) 85 * -o <outputfile> Set name of output file to <outputfile> 86 * -P Compile source through preprocessor only, output to .i file 87 * -p Compile for profiling with prof 88 * -Q[y|n] Emit/don't emit identification info to output file 89 * -R<dir[:dir]> Build runtime search path list into executable 90 * -S Compile and only generate assembly code (.s) 91 * -s Strip symbol table from the executable file 92 * -t Turn off duplicate symbol warnings when linking 93 * -U<name> Delete initial definition of preprocessor symbol <name> 94 * -V Report version number of each compilation phase 95 * -v Do stricter semantic checking 96 * -W<c>,<arg> Pass <arg> to specified component <c> (a,l,m,p,0,2,h,i,u) 97 * -w Suppress compiler warning messages 98 * -Xa Compile assuming ANSI C conformance, allow K & R extensions 99 * (default mode) 100 * -Xs Compile assuming (pre-ANSI) K & R C style code 101 * -Xt Compile assuming K & R conformance, allow ANSI C 102 * -xarch=<a> Specify target architecture instruction set 103 * -xbuiltin[=<b>] When profitable inline, or substitute intrinisic functions 104 * for system functions, b={%all,%none} 105 * -xCC Accept C++ style comments 106 * -xchip=<c> Specify the target processor for use by the optimizer 107 * -xcode=<c> Generate different code for forming addresses 108 * -xcrossfile[=<n>] Enable optimization and inlining across source files, 109 * n={0|1} 110 * -xe Perform only syntax/semantic checking, no code generation 111 * -xF Compile for later mapfile reordering or unused section 112 * elimination 113 * -xhelp=<f> Display on-line help information f(flags, readme, errors) 114 * -xildoff Cancel -xildon 115 * -xildon Enable use of the incremental linker, ild 116 * -xinline=[<a>,...,<a>] Attempt inlining of specified user routines, 117 * <a>={%auto,func,no%func} 118 * -xlibmieee Force IEEE 754 return values for math routines in 119 * exceptional cases 120 * -xlibmil Inline selected libm math routines for optimization 121 * -xlic_lib=sunperf Link in the Sun supplied performance libraries 122 * -xlicinfo Show license server information 123 * -xmaxopt=[off,1,2,3,4,5] maximum optimization level allowed on #pragma opt 124 * -xO<n> Generate optimized code (n={1|2|3|4|5}) 125 * -xP Print prototypes for function definitions 126 * -xprofile=<p> Collect data for a profile or use a profile to optimize 127 * <p>={{collect,use}[:<path>],tcov} 128 * -xregs=<r> Control register allocation 129 * -xs Allow debugging without object (.o) files 130 * -xsb Compile for use with the WorkShop source browser 131 * -xsbfast Generate only WorkShop source browser info, no compilation 132 * -xsfpconst Represent unsuffixed floating point constants as single 133 * precision 134 * -xspace Do not do optimizations that increase code size 135 * -xstrconst Place string literals into read-only data segment 136 * -xtarget=<t> Specify target system for optimization 137 * -xtemp=<dir> Set directory for temporary files to <dir> 138 * -xtime Report the execution time for each compilation phase 139 * -xunroll=n Enable unrolling loops n times where possible 140 * -Y<c>,<dir> Specify <dir> for location of component <c> (a,l,m,p,0,h,i,u) 141 * -YA,<dir> Change default directory searched for components 142 * -YI,<dir> Change default directory searched for include files 143 * -YP,<dir> Change default directory for finding libraries files 144 * -YS,<dir> Change default directory for startup object files 145 */ 146 147 /* 148 * Translation table: 149 */ 150 /* 151 * -# -v 152 * -### error 153 * -A<name[(tokens)]> pass-thru 154 * -B<[static|dynamic]> pass-thru (syntax error for anything else) 155 * -C pass-thru 156 * -c pass-thru 157 * -cg92 -m32 -mcpu=v8 -mtune=supersparc (SPARC only) 158 * -D<name[=token]> pass-thru 159 * -E pass-thru 160 * -erroff=E_EMPTY_TRANSLATION_UNIT ignore 161 * -errtags=%all -Wall 162 * -errwarn=%all -Werror else -Wno-error 163 * -fast error 164 * -fd error 165 * -fnonstd error 166 * -fns[=<yes|no>] error 167 * -fprecision=<p> error 168 * -fround=<r> error 169 * -fsimple[=<n>] error 170 * -fsingle[=<n>] error 171 * -ftrap=<t> error 172 * -fstore error 173 * -g pass-thru 174 * -H pass-thru 175 * -h <name> pass-thru 176 * -I<dir> pass-thru 177 * -i pass-thru 178 * -keeptmp -save-temps 179 * -L<dir> pass-thru 180 * -l<name> pass-thru 181 * -mc error 182 * -mr error 183 * -mr,"string" error 184 * -mt -D_REENTRANT 185 * -native error 186 * -nofstore error 187 * -nolib -nodefaultlibs 188 * -norunpath ignore 189 * -O -O1 (Check the man page to be certain) 190 * -o <outputfile> pass-thru 191 * -P -E -o filename.i (or error) 192 * -p pass-thru 193 * -Q[y|n] error 194 * -R<dir[:dir]> pass-thru 195 * -S pass-thru 196 * -U<name> pass-thru 197 * -V --version 198 * -v -Wall 199 * -Wa,<arg> pass-thru 200 * -Wp,<arg> pass-thru except -xc99=<a> 201 * -Wl,<arg> pass-thru 202 * -W{m,0,2,h,i,u> error/ignore 203 * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone 204 * -Wu,-save_args -msave-args 205 * -w pass-thru 206 * -Xa -std=iso9899:199409 or -ansi 207 * -Xt error 208 * -Xs -traditional -std=c89 209 * -xarch=<a> table 210 * -xbuiltin[=<b>] -fbuiltin (-fno-builtin otherwise) 211 * -xCC ignore 212 * -xchip=<c> table 213 * -xcode=<c> table 214 * -xcrossfile[=<n>] ignore 215 * -xe error 216 * -xF error 217 * -xhelp=<f> error 218 * -xildoff ignore 219 * -xildon ignore 220 * -xinline ignore 221 * -xlibmieee error 222 * -xlibmil error 223 * -xlic_lib=sunperf error 224 * -xmaxopt=[...] error 225 * -xO<n> -O<n> 226 * -xP error 227 * -xprofile=<p> error 228 * -xregs=<r> table 229 * -xs error 230 * -xsb error 231 * -xsbfast error 232 * -xsfpconst error 233 * -xspace ignore (-not -Os) 234 * -xstrconst ignore 235 * -xtarget=<t> table 236 * -xtemp=<dir> error 237 * -xtime error 238 * -xtransition -Wtransition 239 * -xunroll=n error 240 * -Y<c>,<dir> error 241 * -YA,<dir> error 242 * -YI,<dir> -nostdinc -I<dir> 243 * -YP,<dir> error 244 * -YS,<dir> error 245 */ 246 247 #include <ctype.h> 248 #include <err.h> 249 #include <errno.h> 250 #include <fcntl.h> 251 #include <getopt.h> 252 #include <stdio.h> 253 #include <stdlib.h> 254 #include <stdbool.h> 255 #include <string.h> 256 #include <unistd.h> 257 #include <dirent.h> 258 259 #include <sys/param.h> 260 #include <sys/stat.h> 261 #include <sys/types.h> 262 #include <sys/utsname.h> 263 #include <sys/wait.h> 264 265 #define CW_F_CXX 0x01 266 #define CW_F_SHADOW 0x02 267 #define CW_F_EXEC 0x04 268 #define CW_F_ECHO 0x08 269 #define CW_F_XLATE 0x10 270 #define CW_F_PROG 0x20 271 272 typedef enum cw_op { 273 CW_O_NONE = 0, 274 CW_O_PREPROCESS, 275 CW_O_COMPILE, 276 CW_O_LINK 277 } cw_op_t; 278 279 struct aelist { 280 struct ae { 281 struct ae *ae_next; 282 char *ae_arg; 283 } *ael_head, *ael_tail; 284 int ael_argc; 285 }; 286 287 typedef enum { 288 GNU, 289 SUN, 290 SMATCH 291 } compiler_style_t; 292 293 typedef struct { 294 char *c_name; 295 char *c_path; 296 compiler_style_t c_style; 297 } cw_compiler_t; 298 299 typedef struct cw_ictx { 300 struct cw_ictx *i_next; 301 cw_compiler_t *i_compiler; 302 char *i_linker; 303 struct aelist *i_ae; 304 uint32_t i_flags; 305 int i_oldargc; 306 char **i_oldargv; 307 pid_t i_pid; 308 char *i_tmpdir; 309 char *i_stderr; 310 } cw_ictx_t; 311 312 /* 313 * Status values to indicate which Studio compiler and associated 314 * flags are being used. 315 */ 316 #define M32 0x01 /* -m32 - only on Studio 12 */ 317 #define M64 0x02 /* -m64 - only on Studio 12 */ 318 #define SS11 0x100 /* Studio 11 */ 319 #define SS12 0x200 /* Studio 12 */ 320 321 #define TRANS_ENTRY 5 322 /* 323 * Translation table definition for the -xarch= flag. The "x_arg" 324 * value is translated into the appropriate gcc flags according 325 * to the values in x_trans[n]. The x_flags indicates what compiler 326 * is being used and what flags have been set via the use of 327 * "x_arg". 328 */ 329 typedef struct xarch_table { 330 char *x_arg; 331 int x_flags; 332 char *x_trans[TRANS_ENTRY]; 333 } xarch_table_t; 334 335 /* 336 * The translation table for the -xarch= flag used in the Studio compilers. 337 */ 338 static const xarch_table_t xtbl[] = { 339 #if defined(__x86) 340 { "generic", SS11, {NULL} }, 341 { "generic64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 342 { "amd64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 343 { "386", SS11, { "-march=i386" } }, 344 { "pentium_pro", SS11, { "-march=pentiumpro" } }, 345 { "sse", SS11, { "-msse", "-mfpmath=sse" } }, 346 { "sse2", SS11, { "-msse2", "-mfpmath=sse" } }, 347 #elif defined(__sparc) 348 { "generic", (SS11|M32), { "-m32", "-mcpu=v8" } }, 349 { "generic64", (SS11|M64), { "-m64", "-mcpu=v9" } }, 350 { "v8", (SS11|M32), { "-m32", "-mcpu=v8", "-mno-v8plus" } }, 351 { "v8plus", (SS11|M32), { "-m32", "-mcpu=v9", "-mv8plus" } }, 352 { "v8plusa", (SS11|M32), { "-m32", "-mcpu=ultrasparc", "-mv8plus", 353 "-mvis" } }, 354 { "v8plusb", (SS11|M32), { "-m32", "-mcpu=ultrasparc3", "-mv8plus", 355 "-mvis" } }, 356 { "v9", (SS11|M64), { "-m64", "-mcpu=v9" } }, 357 { "v9a", (SS11|M64), { "-m64", "-mcpu=ultrasparc", "-mvis" } }, 358 { "v9b", (SS11|M64), { "-m64", "-mcpu=ultrasparc3", "-mvis" } }, 359 { "sparc", SS12, { "-mcpu=v9", "-mv8plus" } }, 360 { "sparcvis", SS12, { "-mcpu=ultrasparc", "-mvis" } }, 361 { "sparcvis2", SS12, { "-mcpu=ultrasparc3", "-mvis" } } 362 #endif 363 }; 364 365 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t); 366 367 static const char *xchip_tbl[] = { 368 #if defined(__x86) 369 "386", "-mtune=i386", NULL, 370 "486", "-mtune=i486", NULL, 371 "pentium", "-mtune=pentium", NULL, 372 "pentium_pro", "-mtune=pentiumpro", NULL, 373 #elif defined(__sparc) 374 "super", "-mtune=supersparc", NULL, 375 "ultra", "-mtune=ultrasparc", NULL, 376 "ultra3", "-mtune=ultrasparc3", NULL, 377 #endif 378 NULL, NULL 379 }; 380 381 static const char *xcode_tbl[] = { 382 #if defined(__sparc) 383 "abs32", "-fno-pic", "-mcmodel=medlow", NULL, 384 "abs44", "-fno-pic", "-mcmodel=medmid", NULL, 385 "abs64", "-fno-pic", "-mcmodel=medany", NULL, 386 "pic13", "-fpic", NULL, 387 "pic32", "-fPIC", NULL, 388 #endif 389 NULL, NULL 390 }; 391 392 static const char *xtarget_tbl[] = { 393 #if defined(__x86) 394 "pentium_pro", "-march=pentiumpro", NULL, 395 #endif /* __x86 */ 396 NULL, NULL 397 }; 398 399 static const char *xregs_tbl[] = { 400 #if defined(__sparc) 401 "appl", "-mapp-regs", NULL, 402 "no%appl", "-mno-app-regs", NULL, 403 "float", "-mfpu", NULL, 404 "no%float", "-mno-fpu", NULL, 405 #endif /* __sparc */ 406 NULL, NULL 407 }; 408 409 static void 410 nomem(void) 411 { 412 errx(1, "out of memory"); 413 } 414 415 static void 416 newae(struct aelist *ael, const char *arg) 417 { 418 struct ae *ae; 419 420 if ((ae = calloc(1, sizeof (*ae))) == NULL) 421 nomem(); 422 ae->ae_arg = strdup(arg); 423 if (ael->ael_tail == NULL) 424 ael->ael_head = ae; 425 else 426 ael->ael_tail->ae_next = ae; 427 ael->ael_tail = ae; 428 ael->ael_argc++; 429 } 430 431 static cw_ictx_t * 432 newictx(void) 433 { 434 cw_ictx_t *ctx = calloc(1, sizeof (cw_ictx_t)); 435 if (ctx) 436 if ((ctx->i_ae = calloc(1, sizeof (struct aelist))) == NULL) { 437 free(ctx); 438 return (NULL); 439 } 440 441 return (ctx); 442 } 443 444 static void 445 error(const char *arg) 446 { 447 errx(2, "error: mapping failed at or near arg '%s'", arg); 448 } 449 450 /* 451 * Add the current favourite set of warnings to the gcc invocation. 452 */ 453 static void 454 warnings(struct aelist *h) 455 { 456 static int warningsonce; 457 458 if (warningsonce++) 459 return; 460 461 /* 462 * Enable as many warnings as exist, then disable those that we never 463 * ever want. 464 */ 465 newae(h, "-Wall"); 466 newae(h, "-Wextra"); 467 } 468 469 static void 470 optim_disable(struct aelist *h, int level) 471 { 472 if (level >= 2) { 473 newae(h, "-fno-strict-aliasing"); 474 newae(h, "-fno-unit-at-a-time"); 475 newae(h, "-fno-optimize-sibling-calls"); 476 } 477 } 478 479 static void 480 Xsmode(struct aelist *h) 481 { 482 static int xsonce; 483 484 if (xsonce++) 485 return; 486 487 newae(h, "-traditional"); 488 newae(h, "-traditional-cpp"); 489 } 490 491 static void 492 usage(void) 493 { 494 extern char *__progname; 495 (void) fprintf(stderr, 496 "usage: %s [-C] [--versions] --primary <compiler> " 497 "[--shadow <compiler>]... -- cflags...\n", 498 __progname); 499 (void) fprintf(stderr, "compilers take the form: name,path,style\n" 500 " - name: a unique name usable in flag specifiers\n" 501 " - path: path to the compiler binary\n" 502 " - style: the style of flags expected: either sun or gnu\n"); 503 exit(2); 504 } 505 506 static int 507 xlate_xtb(struct aelist *h, const char *xarg) 508 { 509 int i, j; 510 511 for (i = 0; i < xtbl_size; i++) { 512 if (strcmp(xtbl[i].x_arg, xarg) == 0) 513 break; 514 } 515 516 /* 517 * At the end of the table and so no matching "arg" entry 518 * found and so this must be a bad -xarch= flag. 519 */ 520 if (i == xtbl_size) 521 error(xarg); 522 523 for (j = 0; j < TRANS_ENTRY; j++) { 524 if (xtbl[i].x_trans[j] != NULL) 525 newae(h, xtbl[i].x_trans[j]); 526 else 527 break; 528 } 529 return (xtbl[i].x_flags); 530 531 } 532 533 static void 534 xlate(struct aelist *h, const char *xarg, const char **table) 535 { 536 while (*table != NULL && strcmp(xarg, *table) != 0) { 537 while (*table != NULL) 538 table++; 539 table++; 540 } 541 542 if (*table == NULL) 543 error(xarg); 544 545 table++; 546 547 while (*table != NULL) { 548 newae(h, *table); 549 table++; 550 } 551 } 552 553 /* 554 * The compiler wants the output file to end in appropriate extension. If 555 * we're generating a name from whole cloth (path == NULL), we assume that 556 * extension to be .o, otherwise we match the extension of the caller. 557 */ 558 static char * 559 discard_file_name(cw_ictx_t *ctx, const char *path) 560 { 561 char *ret, *ext; 562 char tmpl[] = "cwXXXXXX"; 563 564 if (path == NULL) { 565 ext = ".o"; 566 } else { 567 ext = strrchr(path, '.'); 568 } 569 570 /* 571 * We need absolute control over where the temporary file goes, since 572 * we rely on it for cleanup so tempnam(3C) and tmpnam(3C) are 573 * inappropriate (they use TMPDIR, preferentially). 574 * 575 * mkstemp(3C) doesn't actually help us, since the temporary file 576 * isn't used by us, only its name. 577 */ 578 if (mktemp(tmpl) == NULL) 579 nomem(); 580 581 (void) asprintf(&ret, "%s/%s%s", ctx->i_tmpdir, tmpl, 582 (ext != NULL) ? ext : ""); 583 584 if (ret == NULL) 585 nomem(); 586 587 return (ret); 588 } 589 590 static bool 591 is_source_file(const char *path) 592 { 593 char *ext = strrchr(path, '.'); 594 595 if ((ext == NULL) || (*(ext + 1) == '\0')) 596 return (false); 597 598 ext += 1; 599 600 if ((strcasecmp(ext, "c") == 0) || 601 (strcmp(ext, "cc") == 0) || 602 (strcmp(ext, "i") == 0) || 603 (strcasecmp(ext, "s") == 0) || 604 (strcmp(ext, "cpp") == 0)) { 605 return (true); 606 } 607 608 return (false); 609 } 610 611 612 static void 613 do_gcc(cw_ictx_t *ctx) 614 { 615 int c; 616 int nolibc = 0; 617 int in_output = 0, seen_o = 0, c_files = 0; 618 cw_op_t op = CW_O_LINK; 619 char *model = NULL; 620 char *nameflag; 621 int mflag = 0; 622 623 if (ctx->i_flags & CW_F_PROG) { 624 newae(ctx->i_ae, "--version"); 625 return; 626 } 627 628 newae(ctx->i_ae, "-fident"); 629 newae(ctx->i_ae, "-finline"); 630 newae(ctx->i_ae, "-fno-inline-functions"); 631 newae(ctx->i_ae, "-fno-builtin"); 632 newae(ctx->i_ae, "-fno-asm"); 633 newae(ctx->i_ae, "-fdiagnostics-show-option"); 634 newae(ctx->i_ae, "-nodefaultlibs"); 635 636 #if defined(__sparc) 637 /* 638 * The SPARC ldd and std instructions require 8-byte alignment of 639 * their address operand. gcc correctly uses them only when the 640 * ABI requires 8-byte alignment; unfortunately we have a number of 641 * pieces of buggy code that doesn't conform to the ABI. This 642 * flag makes gcc work more like Studio with -xmemalign=4. 643 */ 644 newae(ctx->i_ae, "-mno-integer-ldd-std"); 645 #endif 646 647 /* 648 * This is needed because 'u' is defined 649 * under a conditional on 'sun'. Should 650 * probably just remove the conditional, 651 * or make it be dependent on '__sun'. 652 * 653 * -Dunix is also missing in enhanced ANSI mode 654 */ 655 newae(ctx->i_ae, "-D__sun"); 656 657 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 658 nomem(); 659 660 /* 661 * Walk the argument list, translating as we go .. 662 */ 663 while (--ctx->i_oldargc > 0) { 664 char *arg = *++ctx->i_oldargv; 665 size_t arglen = strlen(arg); 666 667 if (*arg == '-') { 668 arglen--; 669 } else { 670 /* 671 * Discard inline files that gcc doesn't grok 672 */ 673 if (!in_output && arglen > 3 && 674 strcmp(arg + arglen - 3, ".il") == 0) 675 continue; 676 677 if (!in_output && is_source_file(arg)) 678 c_files++; 679 680 /* 681 * Otherwise, filenames and partial arguments 682 * are passed through for gcc to chew on. However, 683 * output is always discarded for the secondary 684 * compiler. 685 */ 686 if ((ctx->i_flags & CW_F_SHADOW) && in_output) { 687 newae(ctx->i_ae, discard_file_name(ctx, arg)); 688 } else { 689 newae(ctx->i_ae, arg); 690 } 691 in_output = 0; 692 continue; 693 } 694 695 if (ctx->i_flags & CW_F_CXX) { 696 if (strncmp(arg, "-_g++=", 6) == 0) { 697 newae(ctx->i_ae, strchr(arg, '=') + 1); 698 continue; 699 } 700 if (strncmp(arg, "-compat=", 8) == 0) { 701 /* discard -compat=4 and -compat=5 */ 702 continue; 703 } 704 if (strcmp(arg, "-Qoption") == 0) { 705 /* discard -Qoption and its two arguments */ 706 if (ctx->i_oldargc < 3) 707 error(arg); 708 ctx->i_oldargc -= 2; 709 ctx->i_oldargv += 2; 710 continue; 711 } 712 if (strcmp(arg, "-xwe") == 0) { 713 /* turn warnings into errors */ 714 newae(ctx->i_ae, "-Werror"); 715 continue; 716 } 717 if (strcmp(arg, "-norunpath") == 0) { 718 /* gcc has no corresponding option */ 719 continue; 720 } 721 if (strcmp(arg, "-nolib") == 0) { 722 /* -nodefaultlibs is on by default */ 723 nolibc = 1; 724 continue; 725 } 726 #if defined(__sparc) 727 if (strcmp(arg, "-cg92") == 0) { 728 mflag |= xlate_xtb(ctx->i_ae, "v8"); 729 xlate(ctx->i_ae, "super", xchip_tbl); 730 continue; 731 } 732 #endif /* __sparc */ 733 } 734 735 switch ((c = arg[1])) { 736 case '_': 737 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 738 (strncmp(arg, "-_gcc=", 6) == 0) || 739 (strncmp(arg, "-_gnu=", 6) == 0)) { 740 newae(ctx->i_ae, strchr(arg, '=') + 1); 741 } 742 break; 743 case '#': 744 if (arglen == 1) { 745 newae(ctx->i_ae, "-v"); 746 break; 747 } 748 error(arg); 749 break; 750 case 'f': 751 if ((strcmp(arg, "-fpic") == 0) || 752 (strcmp(arg, "-fPIC") == 0)) { 753 newae(ctx->i_ae, arg); 754 break; 755 } 756 error(arg); 757 break; 758 case 'E': 759 if (arglen == 1) { 760 newae(ctx->i_ae, "-xc"); 761 newae(ctx->i_ae, arg); 762 op = CW_O_PREPROCESS; 763 nolibc = 1; 764 break; 765 } 766 error(arg); 767 break; 768 case 'c': 769 case 'S': 770 if (arglen == 1) { 771 op = CW_O_COMPILE; 772 nolibc = 1; 773 } 774 /* FALLTHROUGH */ 775 case 'C': 776 case 'H': 777 case 'p': 778 if (arglen == 1) { 779 newae(ctx->i_ae, arg); 780 break; 781 } 782 error(arg); 783 break; 784 case 'A': 785 case 'g': 786 case 'h': 787 case 'I': 788 case 'i': 789 case 'L': 790 case 'l': 791 case 'R': 792 case 'U': 793 case 'u': 794 case 'w': 795 newae(ctx->i_ae, arg); 796 break; 797 case 'o': 798 seen_o = 1; 799 if (arglen == 1) { 800 in_output = 1; 801 newae(ctx->i_ae, arg); 802 } else if (ctx->i_flags & CW_F_SHADOW) { 803 newae(ctx->i_ae, "-o"); 804 newae(ctx->i_ae, discard_file_name(ctx, arg)); 805 } else { 806 newae(ctx->i_ae, arg); 807 } 808 break; 809 case 'D': 810 newae(ctx->i_ae, arg); 811 /* 812 * XXX Clearly a hack ... do we need _KADB too? 813 */ 814 if (strcmp(arg, "-D_KERNEL") == 0 || 815 strcmp(arg, "-D_BOOT") == 0) 816 newae(ctx->i_ae, "-ffreestanding"); 817 break; 818 case 'd': 819 if (strcmp(arg, "-dalign") == 0) { 820 /* 821 * -dalign forces alignment in some cases; 822 * gcc does not need any flag to do this. 823 */ 824 break; 825 } 826 error(arg); 827 break; 828 case 'e': 829 if (strcmp(arg, 830 "-erroff=E_EMPTY_TRANSLATION_UNIT") == 0) { 831 /* 832 * Accept but ignore this -- gcc doesn't 833 * seem to complain about empty translation 834 * units 835 */ 836 break; 837 } 838 /* XX64 -- ignore all -erroff= options, for now */ 839 if (strncmp(arg, "-erroff=", 8) == 0) 840 break; 841 if (strcmp(arg, "-errtags=yes") == 0) { 842 warnings(ctx->i_ae); 843 break; 844 } 845 if (strcmp(arg, "-errwarn=%all") == 0) { 846 newae(ctx->i_ae, "-Werror"); 847 break; 848 } 849 error(arg); 850 break; 851 case 'k': 852 if (strcmp(arg, "-keeptmp") == 0) { 853 newae(ctx->i_ae, "-save-temps"); 854 break; 855 } 856 error(arg); 857 break; 858 case 'm': 859 if (strcmp(arg, "-mt") == 0) { 860 newae(ctx->i_ae, "-D_REENTRANT"); 861 break; 862 } 863 if (strcmp(arg, "-m64") == 0) { 864 newae(ctx->i_ae, "-m64"); 865 #if defined(__x86) 866 newae(ctx->i_ae, "-mtune=opteron"); 867 #endif 868 mflag |= M64; 869 break; 870 } 871 if (strcmp(arg, "-m32") == 0) { 872 newae(ctx->i_ae, "-m32"); 873 mflag |= M32; 874 break; 875 } 876 error(arg); 877 break; 878 case 'O': 879 if (arglen == 1) { 880 newae(ctx->i_ae, "-O"); 881 break; 882 } 883 error(arg); 884 break; 885 case 'P': 886 /* 887 * We could do '-E -o filename.i', but that's hard, 888 * and we don't need it for the case that's triggering 889 * this addition. We'll require the user to specify 890 * -o in the Makefile. If they don't they'll find out 891 * in a hurry. 892 */ 893 newae(ctx->i_ae, "-E"); 894 op = CW_O_PREPROCESS; 895 nolibc = 1; 896 break; 897 case 's': 898 if (strcmp(arg, "-shared") == 0) { 899 newae(ctx->i_ae, "-shared"); 900 nolibc = 1; 901 break; 902 } 903 error(arg); 904 break; 905 906 case 'V': 907 if (arglen == 1) { 908 ctx->i_flags &= ~CW_F_ECHO; 909 newae(ctx->i_ae, "--version"); 910 break; 911 } 912 error(arg); 913 break; 914 case 'v': 915 if (arglen == 1) { 916 warnings(ctx->i_ae); 917 break; 918 } 919 error(arg); 920 break; 921 case 'W': 922 if (strncmp(arg, "-Wp,-xc99", 9) == 0) { 923 /* 924 * gcc's preprocessor will accept c99 925 * regardless, so accept and ignore. 926 */ 927 break; 928 } 929 if (strncmp(arg, "-Wa,", 4) == 0 || 930 strncmp(arg, "-Wp,", 4) == 0 || 931 strncmp(arg, "-Wl,", 4) == 0) { 932 newae(ctx->i_ae, arg); 933 break; 934 } 935 if (strcmp(arg, "-W0,-noglobal") == 0 || 936 strcmp(arg, "-W0,-xglobalstatic") == 0) { 937 /* 938 * gcc doesn't prefix local symbols 939 * in debug mode, so this is not needed. 940 */ 941 break; 942 } 943 if (strcmp(arg, "-W0,-Lt") == 0) { 944 /* 945 * Generate tests at the top of loops. 946 * There is no direct gcc equivalent, ignore. 947 */ 948 break; 949 } 950 if (strcmp(arg, "-W2,-xwrap_int") == 0) { 951 /* 952 * Use the legacy behaviour (pre-SS11) 953 * for integer wrapping. 954 * gcc does not need this. 955 */ 956 break; 957 } 958 if (strcmp(arg, "-Wd,-xsafe=unboundsym") == 0) { 959 /* 960 * Prevents optimizing away checks for 961 * unbound weak symbol addresses. gcc does 962 * not do this, so it's not needed. 963 */ 964 break; 965 } 966 if (strncmp(arg, "-Wc,-xcode=", 11) == 0) { 967 xlate(ctx->i_ae, arg + 11, xcode_tbl); 968 break; 969 } 970 if (strncmp(arg, "-Wc,-Qiselect", 13) == 0) { 971 /* 972 * Prevents insertion of register symbols. 973 * gcc doesn't do this, so ignore it. 974 */ 975 break; 976 } 977 if (strcmp(arg, "-Wc,-Qassembler-ounrefsym=0") == 0) { 978 /* 979 * Prevents optimizing away of static variables. 980 * gcc does not do this, so it's not needed. 981 */ 982 break; 983 } 984 #if defined(__x86) 985 if (strcmp(arg, "-Wu,-save_args") == 0) { 986 newae(ctx->i_ae, "-msave-args"); 987 break; 988 } 989 #endif /* __x86 */ 990 error(arg); 991 break; 992 case 'X': 993 if (strcmp(arg, "-Xa") == 0 || 994 strcmp(arg, "-Xt") == 0) { 995 break; 996 } 997 if (strcmp(arg, "-Xs") == 0) { 998 Xsmode(ctx->i_ae); 999 break; 1000 } 1001 error(arg); 1002 break; 1003 case 'x': 1004 if (arglen == 1) 1005 error(arg); 1006 switch (arg[2]) { 1007 case 'a': 1008 if (strncmp(arg, "-xarch=", 7) == 0) { 1009 mflag |= xlate_xtb(ctx->i_ae, arg + 7); 1010 break; 1011 } 1012 error(arg); 1013 break; 1014 case 'b': 1015 if (strncmp(arg, "-xbuiltin=", 10) == 0) { 1016 if (strcmp(arg + 10, "%all")) 1017 newae(ctx->i_ae, "-fbuiltin"); 1018 break; 1019 } 1020 error(arg); 1021 break; 1022 case 'C': 1023 /* Accept C++ style comments -- ignore */ 1024 if (strcmp(arg, "-xCC") == 0) 1025 break; 1026 error(arg); 1027 break; 1028 case 'c': 1029 if (strncmp(arg, "-xc99=%all", 10) == 0) { 1030 newae(ctx->i_ae, "-std=gnu99"); 1031 break; 1032 } 1033 if (strncmp(arg, "-xc99=%none", 11) == 0) { 1034 newae(ctx->i_ae, "-std=gnu89"); 1035 break; 1036 } 1037 if (strncmp(arg, "-xchip=", 7) == 0) { 1038 xlate(ctx->i_ae, arg + 7, xchip_tbl); 1039 break; 1040 } 1041 if (strncmp(arg, "-xcode=", 7) == 0) { 1042 xlate(ctx->i_ae, arg + 7, xcode_tbl); 1043 break; 1044 } 1045 if (strncmp(arg, "-xcrossfile", 11) == 0) 1046 break; 1047 error(arg); 1048 break; 1049 case 'F': 1050 /* 1051 * Compile for mapfile reordering, or unused 1052 * section elimination, syntax can be -xF or 1053 * more complex, like -xF=%all -- ignore. 1054 */ 1055 if (strncmp(arg, "-xF", 3) == 0) 1056 break; 1057 error(arg); 1058 break; 1059 case 'i': 1060 if (strncmp(arg, "-xinline", 8) == 0) 1061 /* No inlining; ignore */ 1062 break; 1063 if (strcmp(arg, "-xildon") == 0 || 1064 strcmp(arg, "-xildoff") == 0) 1065 /* No incremental linking; ignore */ 1066 break; 1067 error(arg); 1068 break; 1069 #if defined(__x86) 1070 case 'm': 1071 if (strcmp(arg, "-xmodel=kernel") == 0) { 1072 newae(ctx->i_ae, "-ffreestanding"); 1073 newae(ctx->i_ae, "-mno-red-zone"); 1074 model = "-mcmodel=kernel"; 1075 nolibc = 1; 1076 break; 1077 } 1078 error(arg); 1079 break; 1080 #endif /* __x86 */ 1081 case 'O': 1082 if (strncmp(arg, "-xO", 3) == 0) { 1083 size_t len = strlen(arg); 1084 char *s = NULL; 1085 int c = *(arg + 3); 1086 int level; 1087 1088 if (len != 4 || !isdigit(c)) 1089 error(arg); 1090 1091 level = atoi(arg + 3); 1092 if (level > 5) 1093 error(arg); 1094 if (level >= 2) { 1095 /* 1096 * For gcc-3.4.x at -O2 we 1097 * need to disable optimizations 1098 * that break ON. 1099 */ 1100 optim_disable(ctx->i_ae, level); 1101 /* 1102 * limit -xO3 to -O2 as well. 1103 */ 1104 level = 2; 1105 } 1106 if (asprintf(&s, "-O%d", level) == -1) 1107 nomem(); 1108 newae(ctx->i_ae, s); 1109 free(s); 1110 break; 1111 } 1112 error(arg); 1113 break; 1114 case 'r': 1115 if (strncmp(arg, "-xregs=", 7) == 0) { 1116 xlate(ctx->i_ae, arg + 7, xregs_tbl); 1117 break; 1118 } 1119 error(arg); 1120 break; 1121 case 's': 1122 if (strcmp(arg, "-xs") == 0 || 1123 strcmp(arg, "-xspace") == 0 || 1124 strcmp(arg, "-xstrconst") == 0) 1125 break; 1126 error(arg); 1127 break; 1128 case 't': 1129 if (strncmp(arg, "-xtarget=", 9) == 0) { 1130 xlate(ctx->i_ae, arg + 9, xtarget_tbl); 1131 break; 1132 } 1133 error(arg); 1134 break; 1135 case 'e': 1136 case 'h': 1137 case 'l': 1138 default: 1139 error(arg); 1140 break; 1141 } 1142 break; 1143 case 'Y': 1144 if (arglen == 1) { 1145 if ((arg = *++ctx->i_oldargv) == NULL || 1146 *arg == '\0') 1147 error("-Y"); 1148 ctx->i_oldargc--; 1149 arglen = strlen(arg + 1); 1150 } else { 1151 arg += 2; 1152 } 1153 /* Just ignore -YS,... for now */ 1154 if (strncmp(arg, "S,", 2) == 0) 1155 break; 1156 if (strncmp(arg, "I,", 2) == 0) { 1157 char *s = strdup(arg); 1158 s[0] = '-'; 1159 s[1] = 'I'; 1160 newae(ctx->i_ae, "-nostdinc"); 1161 newae(ctx->i_ae, s); 1162 free(s); 1163 break; 1164 } 1165 error(arg); 1166 break; 1167 case 'Q': 1168 /* 1169 * We could map -Qy into -Wl,-Qy etc. 1170 */ 1171 default: 1172 error(arg); 1173 break; 1174 } 1175 } 1176 1177 free(nameflag); 1178 1179 /* 1180 * When compiling multiple source files in a single invocation some 1181 * compilers output objects into the current directory with 1182 * predictable and conventional names. 1183 * 1184 * We prevent any attempt to compile multiple files at once so that 1185 * any such objects created by a shadow can't escape into a later 1186 * link-edit. 1187 */ 1188 if (c_files > 1 && op != CW_O_PREPROCESS) { 1189 errx(2, "multiple source files are " 1190 "allowed only with -E or -P"); 1191 } 1192 1193 /* 1194 * Make sure that we do not have any unintended interactions between 1195 * the xarch options passed in and the version of the Studio compiler 1196 * used. 1197 */ 1198 if ((mflag & (SS11|SS12)) == (SS11|SS12)) { 1199 errx(2, 1200 "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n"); 1201 } 1202 1203 switch (mflag) { 1204 case 0: 1205 /* FALLTHROUGH */ 1206 case M32: 1207 #if defined(__sparc) 1208 /* 1209 * Only -m32 is defined and so put in the missing xarch 1210 * translation. 1211 */ 1212 newae(ctx->i_ae, "-mcpu=v8"); 1213 newae(ctx->i_ae, "-mno-v8plus"); 1214 #endif 1215 break; 1216 case M64: 1217 #if defined(__sparc) 1218 /* 1219 * Only -m64 is defined and so put in the missing xarch 1220 * translation. 1221 */ 1222 newae(ctx->i_ae, "-mcpu=v9"); 1223 #endif 1224 break; 1225 case SS12: 1226 #if defined(__sparc) 1227 /* no -m32/-m64 flag used - this is an error for sparc builds */ 1228 (void) fprintf(stderr, "No -m32/-m64 flag defined\n"); 1229 exit(2); 1230 #endif 1231 break; 1232 case SS11: 1233 /* FALLTHROUGH */ 1234 case (SS11|M32): 1235 case (SS11|M64): 1236 break; 1237 case (SS12|M32): 1238 #if defined(__sparc) 1239 /* 1240 * Need to add in further 32 bit options because with SS12 1241 * the xarch=sparcvis option can be applied to 32 or 64 1242 * bit, and so the translatation table (xtbl) cannot handle 1243 * that. 1244 */ 1245 newae(ctx->i_ae, "-mv8plus"); 1246 #endif 1247 break; 1248 case (SS12|M64): 1249 break; 1250 default: 1251 (void) fprintf(stderr, 1252 "Incompatible -xarch= and/or -m32/-m64 options used.\n"); 1253 exit(2); 1254 } 1255 1256 if (ctx->i_flags & CW_F_SHADOW) { 1257 if (op == CW_O_PREPROCESS) 1258 exit(0); 1259 else if (op == CW_O_LINK && c_files == 0) 1260 exit(0); 1261 } 1262 1263 if (model != NULL) 1264 newae(ctx->i_ae, model); 1265 if (!nolibc) 1266 newae(ctx->i_ae, "-lc"); 1267 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 1268 newae(ctx->i_ae, "-o"); 1269 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 1270 } 1271 } 1272 1273 static void 1274 do_smatch(cw_ictx_t *ctx) 1275 { 1276 if (ctx->i_flags & CW_F_PROG) { 1277 newae(ctx->i_ae, "--version"); 1278 return; 1279 } 1280 1281 /* 1282 * Some sources shouldn't run smatch at all. 1283 */ 1284 for (int i = 0; i < ctx->i_oldargc; i++) { 1285 char *arg = ctx->i_oldargv[i]; 1286 1287 if (strcmp(arg, "-_smatch=off") == 0) { 1288 ctx->i_flags &= ~ (CW_F_EXEC | CW_F_ECHO); 1289 return; 1290 } 1291 } 1292 1293 /* 1294 * smatch can handle gcc's options. 1295 */ 1296 do_gcc(ctx); 1297 } 1298 1299 static void 1300 do_cc(cw_ictx_t *ctx) 1301 { 1302 int in_output = 0, seen_o = 0, c_files = 0; 1303 cw_op_t op = CW_O_LINK; 1304 char *nameflag; 1305 1306 if (ctx->i_flags & CW_F_PROG) { 1307 newae(ctx->i_ae, "-V"); 1308 return; 1309 } 1310 1311 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 1312 nomem(); 1313 1314 while (--ctx->i_oldargc > 0) { 1315 char *arg = *++ctx->i_oldargv; 1316 1317 if (strncmp(arg, "-_CC=", 5) == 0) { 1318 newae(ctx->i_ae, strchr(arg, '=') + 1); 1319 continue; 1320 } 1321 1322 if (*arg != '-') { 1323 if (!in_output && is_source_file(arg)) 1324 c_files++; 1325 1326 if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) { 1327 newae(ctx->i_ae, arg); 1328 } else { 1329 in_output = 0; 1330 newae(ctx->i_ae, discard_file_name(ctx, arg)); 1331 } 1332 continue; 1333 } 1334 switch (*(arg + 1)) { 1335 case '_': 1336 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 1337 (strncmp(arg, "-_cc=", 5) == 0) || 1338 (strncmp(arg, "-_sun=", 6) == 0)) { 1339 newae(ctx->i_ae, strchr(arg, '=') + 1); 1340 } 1341 break; 1342 1343 case 'V': 1344 ctx->i_flags &= ~CW_F_ECHO; 1345 newae(ctx->i_ae, arg); 1346 break; 1347 case 'o': 1348 seen_o = 1; 1349 if (strlen(arg) == 2) { 1350 in_output = 1; 1351 newae(ctx->i_ae, arg); 1352 } else if (ctx->i_flags & CW_F_SHADOW) { 1353 newae(ctx->i_ae, "-o"); 1354 newae(ctx->i_ae, discard_file_name(ctx, arg)); 1355 } else { 1356 newae(ctx->i_ae, arg); 1357 } 1358 break; 1359 case 'c': 1360 case 'S': 1361 if (strlen(arg) == 2) 1362 op = CW_O_COMPILE; 1363 newae(ctx->i_ae, arg); 1364 break; 1365 case 'E': 1366 case 'P': 1367 if (strlen(arg) == 2) 1368 op = CW_O_PREPROCESS; 1369 /*FALLTHROUGH*/ 1370 default: 1371 newae(ctx->i_ae, arg); 1372 } 1373 } 1374 1375 free(nameflag); 1376 1377 /* See the comment on this same code in do_gcc() */ 1378 if (c_files > 1 && op != CW_O_PREPROCESS) { 1379 errx(2, "multiple source files are " 1380 "allowed only with -E or -P"); 1381 } 1382 1383 if (ctx->i_flags & CW_F_SHADOW) { 1384 if (op == CW_O_PREPROCESS) 1385 exit(0); 1386 else if (op == CW_O_LINK && c_files == 0) 1387 exit(0); 1388 } 1389 1390 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 1391 newae(ctx->i_ae, "-o"); 1392 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 1393 } 1394 } 1395 1396 static void 1397 prepctx(cw_ictx_t *ctx) 1398 { 1399 newae(ctx->i_ae, ctx->i_compiler->c_path); 1400 1401 if (ctx->i_flags & CW_F_PROG) { 1402 (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ? 1403 "shadow" : "primary", ctx->i_compiler->c_path); 1404 (void) fflush(stdout); 1405 } 1406 1407 /* 1408 * If LD_ALTEXEC is already set, the expectation would be that that 1409 * link-editor is run, as such we need to leave it the environment 1410 * alone and let that happen. 1411 */ 1412 if ((ctx->i_linker != NULL) && (getenv("LD_ALTEXEC") == NULL)) 1413 setenv("LD_ALTEXEC", ctx->i_linker, 1); 1414 1415 if (!(ctx->i_flags & CW_F_XLATE)) 1416 return; 1417 1418 switch (ctx->i_compiler->c_style) { 1419 case SUN: 1420 do_cc(ctx); 1421 break; 1422 case GNU: 1423 do_gcc(ctx); 1424 break; 1425 case SMATCH: 1426 do_smatch(ctx); 1427 break; 1428 } 1429 } 1430 1431 static int 1432 invoke(cw_ictx_t *ctx) 1433 { 1434 char **newargv, *makeflags; 1435 int ac; 1436 struct ae *a; 1437 1438 newargv = calloc(ctx->i_ae->ael_argc + 1, sizeof (*newargv)); 1439 if (newargv == NULL) 1440 nomem(); 1441 1442 /* 1443 * Check to see if the silent make flag is present (-s), if so, do not 1444 * echo. The MAKEFLAGS environment variable is set by dmake. By 1445 * observation it appears to place short flags without any arguments 1446 * first followed by any long form flags or flags with arguments. 1447 */ 1448 makeflags = getenv("MAKEFLAGS"); 1449 if (makeflags != NULL) { 1450 size_t makeflags_len = strlen(makeflags); 1451 for (size_t i = 0; i < makeflags_len; i++) { 1452 if (makeflags[i] == 's') { 1453 ctx->i_flags &= ~CW_F_ECHO; 1454 break; 1455 } 1456 /* end of short flags */ 1457 if (makeflags[i] == ' ') 1458 break; 1459 } 1460 } 1461 1462 if (ctx->i_flags & CW_F_ECHO) 1463 (void) fprintf(stderr, "+ "); 1464 1465 for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) { 1466 newargv[ac] = a->ae_arg; 1467 if (ctx->i_flags & CW_F_ECHO) 1468 (void) fprintf(stderr, "%s ", a->ae_arg); 1469 if (a == ctx->i_ae->ael_tail) 1470 break; 1471 } 1472 1473 if (ctx->i_flags & CW_F_ECHO) { 1474 (void) fprintf(stderr, "\n"); 1475 (void) fflush(stderr); 1476 } 1477 1478 if (!(ctx->i_flags & CW_F_EXEC)) 1479 return (0); 1480 1481 /* 1482 * We must fix up the environment here so that the dependency files are 1483 * not trampled by the shadow compiler. Also take care of GCC 1484 * environment variables that will throw off gcc. This assumes a primary 1485 * gcc. 1486 */ 1487 if ((ctx->i_flags & CW_F_SHADOW) && 1488 (unsetenv("SUNPRO_DEPENDENCIES") != 0 || 1489 unsetenv("DEPENDENCIES_OUTPUT") != 0 || 1490 unsetenv("GCC_ROOT") != 0)) { 1491 (void) fprintf(stderr, "error: environment setup failed: %s\n", 1492 strerror(errno)); 1493 return (-1); 1494 } 1495 1496 (void) execv(newargv[0], newargv); 1497 warn("couldn't run %s", newargv[0]); 1498 1499 return (-1); 1500 } 1501 1502 static int 1503 reap(cw_ictx_t *ctx) 1504 { 1505 int status, ret = 0; 1506 char buf[1024]; 1507 struct stat s; 1508 1509 /* 1510 * Only wait for one specific child. 1511 */ 1512 if (ctx->i_pid <= 0) 1513 return (-1); 1514 1515 do { 1516 if (waitpid(ctx->i_pid, &status, 0) < 0) { 1517 warn("cannot reap child"); 1518 return (-1); 1519 } 1520 if (status != 0) { 1521 if (WIFSIGNALED(status)) { 1522 ret = -WTERMSIG(status); 1523 break; 1524 } else if (WIFEXITED(status)) { 1525 ret = WEXITSTATUS(status); 1526 break; 1527 } 1528 } 1529 } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 1530 1531 if (stat(ctx->i_stderr, &s) < 0) { 1532 warn("stat failed on child cleanup"); 1533 return (-1); 1534 } 1535 if (s.st_size != 0) { 1536 FILE *f; 1537 1538 if ((f = fopen(ctx->i_stderr, "r")) != NULL) { 1539 while (fgets(buf, sizeof (buf), f)) 1540 (void) fprintf(stderr, "%s", buf); 1541 (void) fflush(stderr); 1542 (void) fclose(f); 1543 } 1544 } 1545 (void) unlink(ctx->i_stderr); 1546 free(ctx->i_stderr); 1547 1548 /* 1549 * cc returns an error code when given -V; we want that to succeed. 1550 */ 1551 if (ctx->i_flags & CW_F_PROG) 1552 return (0); 1553 1554 return (ret); 1555 } 1556 1557 static int 1558 exec_ctx(cw_ictx_t *ctx, int block) 1559 { 1560 if ((ctx->i_stderr = tempnam(ctx->i_tmpdir, "cw")) == NULL) { 1561 nomem(); 1562 return (-1); 1563 } 1564 1565 if ((ctx->i_pid = fork()) == 0) { 1566 int fd; 1567 1568 (void) fclose(stderr); 1569 if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL, 1570 0666)) < 0) { 1571 err(1, "open failed for standard error"); 1572 } 1573 if (dup2(fd, 2) < 0) { 1574 err(1, "dup2 failed for standard error"); 1575 } 1576 if (fd != 2) 1577 (void) close(fd); 1578 if (freopen("/dev/fd/2", "w", stderr) == NULL) { 1579 err(1, "freopen failed for /dev/fd/2"); 1580 } 1581 1582 prepctx(ctx); 1583 exit(invoke(ctx)); 1584 } 1585 1586 if (ctx->i_pid < 0) { 1587 err(1, "fork failed"); 1588 } 1589 1590 if (block) 1591 return (reap(ctx)); 1592 1593 return (0); 1594 } 1595 1596 static void 1597 parse_compiler(const char *spec, cw_compiler_t *compiler) 1598 { 1599 char *tspec, *token; 1600 1601 if ((tspec = strdup(spec)) == NULL) 1602 nomem(); 1603 1604 if ((token = strsep(&tspec, ",")) == NULL) 1605 errx(1, "Compiler is missing a name: %s", spec); 1606 compiler->c_name = token; 1607 1608 if ((token = strsep(&tspec, ",")) == NULL) 1609 errx(1, "Compiler is missing a path: %s", spec); 1610 compiler->c_path = token; 1611 1612 if ((token = strsep(&tspec, ",")) == NULL) 1613 errx(1, "Compiler is missing a style: %s", spec); 1614 1615 if ((strcasecmp(token, "gnu") == 0) || 1616 (strcasecmp(token, "gcc") == 0)) { 1617 compiler->c_style = GNU; 1618 } else if ((strcasecmp(token, "sun") == 0) || 1619 (strcasecmp(token, "cc") == 0)) { 1620 compiler->c_style = SUN; 1621 } else if ((strcasecmp(token, "smatch") == 0)) { 1622 compiler->c_style = SMATCH; 1623 } else { 1624 errx(1, "unknown compiler style: %s", token); 1625 } 1626 1627 if (tspec != NULL) 1628 errx(1, "Excess tokens in compiler: %s", spec); 1629 } 1630 1631 static void 1632 cleanup(cw_ictx_t *ctx) 1633 { 1634 DIR *dirp; 1635 struct dirent *dp; 1636 char buf[MAXPATHLEN]; 1637 1638 if ((dirp = opendir(ctx->i_tmpdir)) == NULL) { 1639 if (errno != ENOENT) { 1640 err(1, "couldn't open temp directory: %s", 1641 ctx->i_tmpdir); 1642 } else { 1643 return; 1644 } 1645 } 1646 1647 errno = 0; 1648 while ((dp = readdir(dirp)) != NULL) { 1649 (void) snprintf(buf, MAXPATHLEN, "%s/%s", ctx->i_tmpdir, 1650 dp->d_name); 1651 1652 if (strcmp(dp->d_name, ".") == 0 || 1653 strcmp(dp->d_name, "..") == 0) { 1654 continue; 1655 } 1656 1657 if (unlink(buf) == -1) 1658 err(1, "failed to unlink temp file: %s", dp->d_name); 1659 errno = 0; 1660 } 1661 1662 if (errno != 0) { 1663 err(1, "failed to read temporary directory: %s", 1664 ctx->i_tmpdir); 1665 } 1666 1667 (void) closedir(dirp); 1668 if (rmdir(ctx->i_tmpdir) != 0) { 1669 err(1, "failed to unlink temporary directory: %s", 1670 ctx->i_tmpdir); 1671 } 1672 } 1673 1674 int 1675 main(int argc, char **argv) 1676 { 1677 int ch; 1678 cw_compiler_t primary = { NULL, NULL, 0 }; 1679 cw_compiler_t shadows[10]; 1680 int nshadows = 0; 1681 int ret = 0; 1682 bool do_serial; 1683 bool do_exec; 1684 bool vflg = false; 1685 bool Cflg = false; 1686 bool cflg = false; 1687 bool nflg = false; 1688 char *tmpdir; 1689 1690 cw_ictx_t *main_ctx; 1691 1692 static struct option longopts[] = { 1693 { "compiler", no_argument, NULL, 'c' }, 1694 { "linker", required_argument, NULL, 'l' }, 1695 { "noecho", no_argument, NULL, 'n' }, 1696 { "primary", required_argument, NULL, 'p' }, 1697 { "shadow", required_argument, NULL, 's' }, 1698 { "versions", no_argument, NULL, 'v' }, 1699 { NULL, 0, NULL, 0 }, 1700 }; 1701 1702 1703 if ((main_ctx = newictx()) == NULL) 1704 nomem(); 1705 1706 while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) { 1707 switch (ch) { 1708 case 'c': 1709 cflg = true; 1710 break; 1711 case 'C': 1712 Cflg = true; 1713 break; 1714 case 'l': 1715 if ((main_ctx->i_linker = strdup(optarg)) == NULL) 1716 nomem(); 1717 break; 1718 case 'n': 1719 nflg = true; 1720 break; 1721 case 'p': 1722 if (primary.c_path != NULL) { 1723 warnx("Only one primary compiler may " 1724 "be specified"); 1725 usage(); 1726 } 1727 1728 parse_compiler(optarg, &primary); 1729 break; 1730 case 's': 1731 if (nshadows >= 10) 1732 errx(1, "May only use 10 shadows at " 1733 "the moment"); 1734 parse_compiler(optarg, &shadows[nshadows]); 1735 nshadows++; 1736 break; 1737 case 'v': 1738 vflg = true; 1739 break; 1740 default: 1741 (void) fprintf(stderr, "Did you forget '--'?\n"); 1742 usage(); 1743 } 1744 } 1745 1746 if (primary.c_path == NULL) { 1747 warnx("A primary compiler must be specified"); 1748 usage(); 1749 } 1750 1751 do_serial = getenv("CW_SHADOW_SERIAL") != NULL; 1752 do_exec = getenv("CW_NO_EXEC") == NULL; 1753 1754 /* Leave room for argv[0] */ 1755 argc -= (optind - 1); 1756 argv += (optind - 1); 1757 1758 main_ctx->i_oldargc = argc; 1759 main_ctx->i_oldargv = argv; 1760 main_ctx->i_flags = CW_F_XLATE; 1761 if (nflg == 0) 1762 main_ctx->i_flags |= CW_F_ECHO; 1763 if (do_exec) 1764 main_ctx->i_flags |= CW_F_EXEC; 1765 if (Cflg) 1766 main_ctx->i_flags |= CW_F_CXX; 1767 main_ctx->i_compiler = &primary; 1768 1769 if (cflg) { 1770 (void) fputs(primary.c_path, stdout); 1771 } 1772 1773 if (vflg) { 1774 (void) printf("cw version %s\n", CW_VERSION); 1775 (void) fflush(stdout); 1776 main_ctx->i_flags &= ~CW_F_ECHO; 1777 main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC; 1778 do_serial = 1; 1779 } 1780 1781 tmpdir = getenv("TMPDIR"); 1782 if (tmpdir == NULL) 1783 tmpdir = "/tmp"; 1784 1785 if (asprintf(&main_ctx->i_tmpdir, "%s/cw.XXXXXX", tmpdir) == -1) 1786 nomem(); 1787 1788 if ((main_ctx->i_tmpdir = mkdtemp(main_ctx->i_tmpdir)) == NULL) 1789 errx(1, "failed to create temporary directory"); 1790 1791 ret |= exec_ctx(main_ctx, do_serial); 1792 1793 for (int i = 0; i < nshadows; i++) { 1794 int r; 1795 cw_ictx_t *shadow_ctx; 1796 1797 if ((shadow_ctx = newictx()) == NULL) 1798 nomem(); 1799 1800 (void) memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t)); 1801 1802 shadow_ctx->i_flags |= CW_F_SHADOW; 1803 shadow_ctx->i_compiler = &shadows[i]; 1804 1805 r = exec_ctx(shadow_ctx, do_serial); 1806 if (r == 0) { 1807 shadow_ctx->i_next = main_ctx->i_next; 1808 main_ctx->i_next = shadow_ctx; 1809 } 1810 ret |= r; 1811 } 1812 1813 if (!do_serial) { 1814 cw_ictx_t *next = main_ctx; 1815 while (next != NULL) { 1816 cw_ictx_t *toreap = next; 1817 next = next->i_next; 1818 ret |= reap(toreap); 1819 } 1820 } 1821 1822 cleanup(main_ctx); 1823 return (ret); 1824 } 1825