/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Wrapper for the GNU C compiler to make it accept the Sun C compiler * arguments where possible. * * Since the translation is inexact, this is something of a work-in-progress. */ /* * -# Verbose mode * -### Show compiler commands built by driver, no compilation * -A Preprocessor predicate assertion * -B<[static|dynamic]> Specify dynamic or static binding * -C Prevent preprocessor from removing comments * -c Compile only - produce .o files, suppress linking * -cg92 Alias for -xtarget=ss1000 * -D Associate name with token as if by #define * -d[y|n] dynamic [-dy] or static [-dn] option to linker * -E Compile source through preprocessor only, output to stdout * -erroff= Suppress warnings specified by tags t(%none, %all, ) * -errtags= Display messages with tags a(no, yes) * -errwarn= Treats warnings specified by tags t(%none, %all, ) * as errors * -fast Optimize using a selection of options * -fd Report old-style function definitions and declarations * -flags Show this summary of compiler options * -fnonstd Initialize floating-point hardware to non-standard preferences * -fns[=] Select non-standard floating point mode * -fprecision=

Set FP rounding precision mode p(single, double, extended) * -fround= Select the IEEE rounding mode in effect at startup * -fsimple[=] Select floating-point optimization preferences * -fsingle Use single-precision arithmetic (-Xt and -Xs modes only) * -ftrap= Select floating-point trapping mode in effect at startup * -fstore force floating pt. values to target precision on assignment * -G Build a dynamic shared library * -g Compile for debugging * -H Print path name of each file included during compilation * -h Assign to generated dynamic shared library * -I

Add to preprocessor #include file search path * -i Passed to linker to ignore any LD_LIBRARY_PATH setting * -keeptmp Keep temporary files created during compilation * -KPIC Compile position independent code with 32-bit addresses * -Kpic Compile position independent code * -L Pass to linker to add to the library search path * -l Link with library lib.a or lib.so * -mc Remove duplicate strings from .comment section of output files * -mr Remove all strings from .comment section of output files * -mr,"string" Remove all strings and append "string" to .comment section * -mt Specify options needed when compiling multi-threaded code * -native Find available processor, generate code accordingly * -nofstore Do not force floating pt. values to target precision * on assignment * -nolib Same as -xnolib * -noqueue Disable queuing of compiler license requests * -norunpath Do not build in a runtime path for shared libraries * -O Use default optimization level (-xO2 or -xO3. Check man page.) * -o Set name of output file to * -P Compile source through preprocessor only, output to .i file * -PIC Alias for -KPIC or -xcode=pic32 * -p Compile for profiling with prof * -pic Alias for -Kpic or -xcode=pic13 * -Q[y|n] Emit/don't emit identification info to output file * -qp Compile for profiling with prof * -R Build runtime search path list into executable * -S Compile and only generate assembly code (.s) * -s Strip symbol table from the executable file * -t Turn off duplicate symbol warnings when linking * -U Delete initial definition of preprocessor symbol * -V Report version number of each compilation phase * -v Do stricter semantic checking * -W, Pass to specified component (a,l,m,p,0,2,h,i,u) * -w Suppress compiler warning messages * -Xa Compile assuming ANSI C conformance, allow K & R extensions * (default mode) * -Xc Compile assuming strict ANSI C conformance * -Xs Compile assuming (pre-ANSI) K & R C style code * -Xt Compile assuming K & R conformance, allow ANSI C * -x386 Generate code for the 80386 processor * -x486 Generate code for the 80486 processor * -xarch= Specify target architecture instruction set * -xbuiltin[=] When profitable inline, or substitute intrinisic functions * for system functions, b={%all,%none} * -xCC Accept C++ style comments * -xchar_byte_order= Specify multi-char byte order (default, high, low) * -xchip= Specify the target processor for use by the optimizer * -xcode= Generate different code for forming addresses * -xcrossfile[=] Enable optimization and inlining across source files, * n={0|1} * -xe Perform only syntax/semantic checking, no code generation * -xF Compile for later mapfile reordering * -xhelp= Display on-line help information f(flags, readme, errors) * -xildoff Cancel -xildon * -xildon Enable use of the incremental linker, ild * -xinline=[,...,] Attempt inlining of specified user routines, * ={%auto,func,no%func} * -xlibmieee Force IEEE 754 return values for math routines in * exceptional cases * -xlibmil Inline selected libm math routines for optimization * -xlic_lib=sunperf Link in the Sun supplied performance libraries * -xlicinfo Show license server information * -xM Generate makefile dependencies * -xM1 Generate makefile dependencies, but exclude /usr/include * -xmaxopt=[off,1,2,3,4,5] maximum optimization level allowed on #pragma opt * -xnolib Do not link with default system libraries * -xnolibmil Cancel -xlibmil on command line * -xO Generate optimized code (n={1|2|3|4|5}) * -xP Print prototypes for function definitions * -xpentium Generate code for the pentium processor * -xpg Compile for profiling with gprof * -xprofile=

Collect data for a profile or use a profile to optimize *

={{collect,use}[:],tcov} * -xregs= Control register allocation * -xs Allow debugging without object (.o) files * -xsb Compile for use with the WorkShop source browser * -xsbfast Generate only WorkShop source browser info, no compilation * -xsfpconst Represent unsuffixed floating point constants as single * precision * -xspace Do not do optimizations that increase code size * -xstrconst Place string literals into read-only data segment * -xtarget= Specify target system for optimization * -xtemp=

Set directory for temporary files to * -xtime Report the execution time for each compilation phase * -xtransition Emit warnings for differences between K&R C and ANSI C * -xtrigraphs[=] Enable|disable trigraph translation * -xunroll=n Enable unrolling loops n times where possible * -Y, Specify for location of component (a,l,m,p,0,h,i,u) * -YA, Change default directory searched for components * -YI, Change default directory searched for include files * -YP, Change default directory for finding libraries files * -YS, Change default directory for startup object files */ /* * Translation table: */ /* * -# -v * -### error * -A pass-thru * -B<[static|dynamic]> pass-thru (syntax error for anything else) * -C pass-thru * -c pass-thru * -cg92 -m32 -mcpu=v8 -mtune=supersparc (SPARC only) * -D pass-thru * -dy or -dn -Wl,-dy or -Wl,-dn * -E pass-thru * -erroff=E_EMPTY_TRANSLATION_UNIT ignore * -errtags=%all -Wall * -errwarn=%all -Werror else -Wno-error * -fast error * -fd error * -flags --help * -fnonstd error * -fns[=] error * -fprecision=

error * -fround= error * -fsimple[=] error * -fsingle[=] error * -ftrap= error * -fstore error * -G pass-thru * -g pass-thru * -H pass-thru * -h pass-thru * -I

pass-thru * -i pass-thru * -keeptmp -save-temps * -KPIC -fPIC * -Kpic -fpic * -L pass-thru * -l pass-thru * -mc error * -mr error * -mr,"string" error * -mt -D_REENTRANT * -native error * -nofstore error * -nolib -nodefaultlibs * -noqueue ignore * -norunpath ignore * -O -O1 (Check the man page to be certain) * -o pass-thru * -P -E -o filename.i (or error) * -PIC -fPIC (C++ only) * -p pass-thru * -pic -fpic (C++ only) * -Q[y|n] error * -qp -p * -R pass-thru * -S pass-thru * -s -Wl,-s * -t -Wl,-t * -U pass-thru * -V --version * -v -Wall * -Wa, pass-thru * -Wp, pass-thru except -xc99= * -Wl, pass-thru * -W{m,0,2,h,i,u> error/ignore * -Wu,-xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone * -Wu,-save_args -msave-args * -w pass-thru * -Xa -std=iso9899:199409 or -ansi * -Xc -ansi -pedantic * -Xt error * -Xs -traditional -std=c89 * -x386 -march=i386 (x86 only) * -x486 -march=i486 (x86 only) * -xarch= table * -xbuiltin[=] -fbuiltin (-fno-builtin otherwise) * -xCC ignore * -xchar_byte_order= error * -xchip= table * -xcode= table * -xdebugformat= ignore (always use dwarf-2 for gcc) * -xcrossfile[=] ignore * -xe error * -xF error * -xhelp= error * -xildoff ignore * -xildon ignore * -xinline ignore * -xlibmieee error * -xlibmil error * -xlic_lib=sunperf error * -xM -M * -xM1 -MM * -xmaxopt=[...] error * -xnolib -nodefaultlibs * -xnolibmil error * -xO -O * -xP error * -xpentium -march=pentium (x86 only) * -xpg error * -xprofile=

error * -xregs= table * -xs error * -xsb error * -xsbfast error * -xsfpconst error * -xspace ignore (-not -Os) * -xstrconst ignore * -xtarget= table * -xtemp=

error * -xtime error * -xtransition -Wtransition * -xtrigraphs= -trigraphs -notrigraphs * -xunroll=n error * -W0,-xdbggen=no%usedonly -fno-eliminate-unused-debug-symbols * -fno-eliminate-unused-debug-types * -Y, error * -YA, error * -YI, -nostdinc -I * -YP, error * -YS, error */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CW_F_CXX 0x01 #define CW_F_SHADOW 0x02 #define CW_F_EXEC 0x04 #define CW_F_ECHO 0x08 #define CW_F_XLATE 0x10 #define CW_F_PROG 0x20 typedef enum cw_compiler { CW_C_CC = 0, CW_C_GCC } cw_compiler_t; static const char *cmds[] = { "cc", "CC", "gcc", "g++" }; static const char *dirs[] = { DEFAULT_CC_DIR, DEFAULT_CPLUSPLUS_DIR, DEFAULT_GCC_DIR, DEFAULT_GPLUSPLUS_DIR }; #define CC(ctx) \ (((ctx)->i_flags & CW_F_SHADOW) ? \ ((ctx)->i_compiler == CW_C_CC ? CW_C_GCC : CW_C_CC) : \ (ctx)->i_compiler) #define CIDX(compiler, flags) \ ((int)(compiler) << 1) + ((flags) & CW_F_CXX ? 1 : 0) typedef enum cw_op { CW_O_NONE = 0, CW_O_PREPROCESS, CW_O_COMPILE, CW_O_LINK } cw_op_t; struct aelist { struct ae { struct ae *ae_next; char *ae_arg; } *ael_head, *ael_tail; int ael_argc; }; typedef struct cw_ictx { cw_compiler_t i_compiler; struct aelist *i_ae; uint32_t i_flags; int i_oldargc; char **i_oldargv; pid_t i_pid; char i_discard[MAXPATHLEN]; char *i_stderr; } cw_ictx_t; static const char *progname; static const char *xarch_tbl[] = { #if defined(__x86) "generic", NULL, "generic64", "-m64", "-mtune=opteron", NULL, "amd64", "-m64", "-mtune=opteron", NULL, "386", "-march=i386", NULL, "pentium_pro", "-march=pentiumpro", NULL, #elif defined(__sparc) "generic", "-m32", "-mcpu=v8", NULL, "generic64", "-m64", "-mcpu=v9", NULL, "v8", "-m32", "-mcpu=v8", "-mno-v8plus", NULL, "v8plus", "-m32", "-mcpu=v9", "-mv8plus", NULL, "v8plusa", "-m32", "-mcpu=ultrasparc", "-mv8plus", "-mvis", NULL, "v8plusb", "-m32", "-mcpu=ultrasparc3", "-mv8plus", "-mvis", NULL, "v9", "-m64", "-mcpu=v9", NULL, "v9a", "-m64", "-mcpu=ultrasparc", "-mvis", NULL, "v9b", "-m64", "-mcpu=ultrasparc3", "-mvis", NULL, #endif NULL, NULL }; static const char *xchip_tbl[] = { #if defined(__x86) "386", "-mtune=i386", NULL, "486", "-mtune=i486", NULL, "pentium", "-mtune=pentium", NULL, "pentium_pro", "-mtune=pentiumpro", NULL, #elif defined(__sparc) "super", "-mtune=supersparc", NULL, "ultra", "-mtune=ultrasparc", NULL, "ultra3", "-mtune=ultrasparc3", NULL, #endif NULL, NULL }; static const char *xcode_tbl[] = { #if defined(__sparc) "abs32", "-fno-pic", "-mcmodel=medlow", NULL, "abs44", "-fno-pic", "-mcmodel=medmid", NULL, "abs64", "-fno-pic", "-mcmodel=medany", NULL, "pic13", "-fpic", NULL, "pic32", "-fPIC", NULL, #endif NULL, NULL }; static const char *xtarget_tbl[] = { #if defined(__x86) "pentium_pro", "-march=pentiumpro", NULL, #endif /* __x86 */ NULL, NULL }; static const char *xregs_tbl[] = { #if defined(__sparc) "appl", "-mapp-regs", NULL, "no%appl", "-mno-app-regs", NULL, "float", "-mfpu", NULL, "no%float", "-mno-fpu", NULL, #endif /* __sparc */ NULL, NULL }; static void nomem(void) { (void) fprintf(stderr, "%s: error: out of memory\n", progname); exit(1); } static void cw_perror(const char *fmt, ...) { va_list ap; int saved_errno = errno; (void) fprintf(stderr, "%s: error: ", progname); va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fprintf(stderr, " (%s)\n", strerror(saved_errno)); } static void newae(struct aelist *ael, const char *arg) { struct ae *ae; if ((ae = calloc(sizeof (*ae), 1)) == NULL) nomem(); ae->ae_arg = strdup(arg); if (ael->ael_tail == NULL) ael->ael_head = ae; else ael->ael_tail->ae_next = ae; ael->ael_tail = ae; ael->ael_argc++; } static cw_ictx_t * newictx(void) { cw_ictx_t *ctx = calloc(sizeof (cw_ictx_t), 1); if (ctx) if ((ctx->i_ae = calloc(sizeof (struct aelist), 1)) == NULL) { free(ctx); return (NULL); } return (ctx); } static void error(const char *arg) { (void) fprintf(stderr, "%s: error: mapping failed at or near arg '%s'\n", progname, arg); exit(2); } /* * Add the current favourite set of warnings to the gcc invocation. */ static void warnings(struct aelist *h) { static int warningsonce; if (warningsonce++) return; newae(h, "-Wall"); newae(h, "-Wno-unknown-pragmas"); newae(h, "-Wno-missing-braces"); newae(h, "-Wno-sign-compare"); newae(h, "-Wno-parentheses"); newae(h, "-Wno-uninitialized"); newae(h, "-Wno-implicit-function-declaration"); newae(h, "-Wno-unused"); newae(h, "-Wno-trigraphs"); newae(h, "-Wno-char-subscripts"); newae(h, "-Wno-switch"); } static void optim_disable(struct aelist *h, int level) { if (level >= 2) { newae(h, "-fno-strict-aliasing"); newae(h, "-fno-unit-at-a-time"); newae(h, "-fno-optimize-sibling-calls"); } } /* ARGSUSED */ static void Xamode(struct aelist *h) { } static void Xcmode(struct aelist *h) { static int xconce; if (xconce++) return; newae(h, "-ansi"); newae(h, "-pedantic-errors"); } static void Xsmode(struct aelist *h) { static int xsonce; if (xsonce++) return; newae(h, "-traditional"); newae(h, "-traditional-cpp"); } static void usage() { (void) fprintf(stderr, "usage: %s { -_cc | -_gcc | -_CC | -_g++ } [ -_compiler | ... ]\n", progname); exit(2); } static void xlate(struct aelist *h, const char *xarg, const char **table) { while (*table != NULL && strcmp(xarg, *table) != 0) { while (*table != NULL) table++; table++; } if (*table == NULL) error(xarg); table++; while (*table != NULL) { newae(h, *table); table++; } } static void do_gcc(cw_ictx_t *ctx) { int c; int pic = 0, nolibc = 0; int in_output = 0, seen_o = 0, c_files = 0; cw_op_t op = CW_O_LINK; char *model = NULL; if (ctx->i_flags & CW_F_PROG) { newae(ctx->i_ae, "--version"); return; } newae(ctx->i_ae, "-fident"); newae(ctx->i_ae, "-finline"); newae(ctx->i_ae, "-fno-inline-functions"); newae(ctx->i_ae, "-fno-builtin"); newae(ctx->i_ae, "-fno-asm"); newae(ctx->i_ae, "-nodefaultlibs"); #if defined(__sparc) /* * The SPARC ldd and std instructions require 8-byte alignment of * their address operand. gcc correctly uses them only when the * ABI requires 8-byte alignment; unfortunately we have a number of * pieces of buggy code that doesn't conform to the ABI. This * flag makes gcc work more like Studio with -xmemalign=4. */ newae(ctx->i_ae, "-mno-integer-ldd-std"); #endif /* * This is needed because 'u' is defined * under a conditional on 'sun'. Should * probably just remove the conditional, * or make it be dependent on '__sun'. * * -Dunix is also missing in enhanced ANSI mode */ newae(ctx->i_ae, "-D__sun"); /* * Walk the argument list, translating as we go .. */ while (--ctx->i_oldargc > 0) { char *arg = *++ctx->i_oldargv; size_t arglen = strlen(arg); if (*arg == '-') { arglen--; } else { /* * Discard inline files that gcc doesn't grok */ if (!in_output && arglen > 3 && strcmp(arg + arglen - 3, ".il") == 0) continue; if (!in_output && arglen > 2 && arg[arglen - 2] == '.' && (arg[arglen - 1] == 'S' || arg[arglen - 1] == 's' || arg[arglen - 1] == 'c' || arg[arglen - 1] == 'i')) c_files++; /* * Otherwise, filenames and partial arguments * are passed through for gcc to chew on. However, * output is always discarded for the secondary * compiler. */ if ((ctx->i_flags & CW_F_SHADOW) && in_output) newae(ctx->i_ae, ctx->i_discard); else newae(ctx->i_ae, arg); in_output = 0; continue; } if (ctx->i_flags & CW_F_CXX) { if (strncmp(arg, "-compat=", 8) == 0) { /* discard -compat=4 and -compat=5 */ continue; } if (strcmp(arg, "-Qoption") == 0) { /* discard -Qoption and its two arguments */ if (ctx->i_oldargc < 3) error(arg); ctx->i_oldargc -= 2; ctx->i_oldargv += 2; continue; } if (strcmp(arg, "-xwe") == 0) { /* turn warnings into errors */ newae(ctx->i_ae, "-Werror"); continue; } if (strcmp(arg, "-noex") == 0) { /* no exceptions */ newae(ctx->i_ae, "-fno-exceptions"); /* no run time type descriptor information */ newae(ctx->i_ae, "-fno-rtti"); continue; } if (strcmp(arg, "-pic") == 0) { newae(ctx->i_ae, "-fpic"); pic = 1; continue; } if (strcmp(arg, "-PIC") == 0) { newae(ctx->i_ae, "-fPIC"); pic = 1; continue; } if (strcmp(arg, "-norunpath") == 0) { /* gcc has no corresponding option */ continue; } if (strcmp(arg, "-nolib") == 0) { /* -nodefaultlibs is on by default */ nolibc = 1; continue; } #if defined(__sparc) if (strcmp(arg, "-cg92") == 0) { xlate(ctx->i_ae, "v8", xarch_tbl); xlate(ctx->i_ae, "super", xchip_tbl); continue; } #endif /* __sparc */ } switch ((c = arg[1])) { case '_': if (strcmp(arg, "-_noecho") == 0) ctx->i_flags &= ~CW_F_ECHO; else if (strncmp(arg, "-_cc=", 5) == 0 || strncmp(arg, "-_CC=", 5) == 0) /* EMPTY */; else if (strncmp(arg, "-_gcc=", 6) == 0 || strncmp(arg, "-_g++=", 6) == 0) newae(ctx->i_ae, arg + 6); else error(arg); break; case '#': if (arglen == 1) { newae(ctx->i_ae, "-v"); break; } error(arg); break; case 'g': newae(ctx->i_ae, "-gdwarf-2"); break; case 'E': if (arglen == 1) { newae(ctx->i_ae, "-xc"); newae(ctx->i_ae, arg); op = CW_O_PREPROCESS; nolibc = 1; break; } error(arg); break; case 'c': case 'S': if (arglen == 1) { op = CW_O_COMPILE; nolibc = 1; } /* FALLTHROUGH */ case 'C': case 'H': case 'p': if (arglen == 1) { newae(ctx->i_ae, arg); break; } error(arg); break; case 'A': case 'h': case 'I': case 'i': case 'L': case 'l': case 'R': case 'U': case 'u': case 'w': newae(ctx->i_ae, arg); break; case 'o': seen_o = 1; if (arglen == 1) { in_output = 1; newae(ctx->i_ae, arg); } else if (ctx->i_flags & CW_F_SHADOW) { newae(ctx->i_ae, "-o"); newae(ctx->i_ae, ctx->i_discard); } else { newae(ctx->i_ae, arg); } break; case 'D': newae(ctx->i_ae, arg); /* * XXX Clearly a hack ... do we need _KADB too? */ if (strcmp(arg, "-D_KERNEL") == 0 || strcmp(arg, "-D_BOOT") == 0) newae(ctx->i_ae, "-ffreestanding"); break; case 'd': if (arglen == 2) { if (strcmp(arg, "-dy") == 0) { newae(ctx->i_ae, "-Wl,-dy"); break; } if (strcmp(arg, "-dn") == 0) { newae(ctx->i_ae, "-Wl,-dn"); break; } } if (strcmp(arg, "-dalign") == 0) { /* * -dalign forces alignment in some cases; * gcc does not need any flag to do this. */ break; } error(arg); break; case 'e': if (strcmp(arg, "-erroff=E_EMPTY_TRANSLATION_UNIT") == 0) { /* * Accept but ignore this -- gcc doesn't * seem to complain about empty translation * units */ break; } /* XX64 -- ignore all -erroff= options, for now */ if (strncmp(arg, "-erroff=", 8) == 0) break; if (strcmp(arg, "-errtags=yes") == 0) { warnings(ctx->i_ae); break; } if (strcmp(arg, "-errwarn=%all") == 0) { newae(ctx->i_ae, "-Werror"); break; } error(arg); break; case 'f': if (strcmp(arg, "-flags") == 0) { newae(ctx->i_ae, "--help"); break; } error(arg); break; case 'G': newae(ctx->i_ae, "-shared"); nolibc = 1; break; case 'k': if (strcmp(arg, "-keeptmp") == 0) { newae(ctx->i_ae, "-save-temps"); break; } error(arg); break; case 'K': if (arglen == 1) { if ((arg = *++ctx->i_oldargv) == NULL || *arg == '\0') error("-K"); ctx->i_oldargc--; } else { arg += 2; } if (strcmp(arg, "pic") == 0) { newae(ctx->i_ae, "-fpic"); pic = 1; break; } if (strcmp(arg, "PIC") == 0) { newae(ctx->i_ae, "-fPIC"); pic = 1; break; } error("-K"); break; case 'm': if (strcmp(arg, "-mt") == 0) { newae(ctx->i_ae, "-D_REENTRANT"); break; } error(arg); break; case 'B': /* linker options */ case 'M': case 'z': { char *opt; size_t len; char *s; if (arglen == 1) { opt = *++ctx->i_oldargv; if (opt == NULL || *opt == '\0') error(arg); ctx->i_oldargc--; } else { opt = arg + 2; } len = strlen(opt) + 7; if ((s = malloc(len)) == NULL) nomem(); (void) snprintf(s, len, "-Wl,-%c%s", c, opt); newae(ctx->i_ae, s); free(s); } break; case 'n': if (strcmp(arg, "-noqueue") == 0) { /* * Horrid license server stuff - n/a */ break; } error(arg); break; case 'O': if (arglen == 1) { newae(ctx->i_ae, "-O"); break; } error(arg); break; case 'P': /* * We could do '-E -o filename.i', but that's hard, * and we don't need it for the case that's triggering * this addition. We'll require the user to specify * -o in the Makefile. If they don't they'll find out * in a hurry. */ newae(ctx->i_ae, "-E"); op = CW_O_PREPROCESS; nolibc = 1; break; case 'q': if (strcmp(arg, "-qp") == 0) { newae(ctx->i_ae, "-p"); break; } error(arg); break; case 's': if (arglen == 1) { newae(ctx->i_ae, "-Wl,-s"); break; } error(arg); break; case 't': if (arglen == 1) { newae(ctx->i_ae, "-Wl,-t"); break; } error(arg); break; case 'V': if (arglen == 1) { ctx->i_flags &= ~CW_F_ECHO; newae(ctx->i_ae, "--version"); break; } error(arg); break; case 'v': if (arglen == 1) { warnings(ctx->i_ae); break; } error(arg); break; case 'W': if (strncmp(arg, "-Wp,-xc99", 9) == 0) { /* * gcc's preprocessor will accept c99 * regardless, so accept and ignore. */ break; } if (strncmp(arg, "-Wa,", 4) == 0 || strncmp(arg, "-Wp,", 4) == 0 || strncmp(arg, "-Wl,", 4) == 0) { newae(ctx->i_ae, arg); break; } if (strcmp(arg, "-W0,-xc99=pragma") == 0) { /* (undocumented) enables _Pragma */ break; } if (strcmp(arg, "-W0,-xc99=%none") == 0) { /* * This is a polite way of saying * "no c99 constructs allowed!" * For now, just accept and ignore this. */ break; } if (strcmp(arg, "-W0,-noglobal") == 0 || strcmp(arg, "-W0,-xglobalstatic") == 0) { /* * gcc doesn't prefix local symbols * in debug mode, so this is not needed. */ break; } if (strcmp(arg, "-W0,-Lt") == 0) { /* * Generate tests at the top of loops. * There is no direct gcc equivalent, ignore. */ break; } if (strcmp(arg, "-W0,-xdbggen=no%usedonly") == 0) { newae(ctx->i_ae, "-fno-eliminate-unused-debug-symbols"); newae(ctx->i_ae, "-fno-eliminate-unused-debug-types"); break; } if (strcmp(arg, "-W2,-xwrap_int") == 0) { /* * Use the legacy behaviour (pre-SS11) * for integer wrapping. * gcc does not need this. */ break; } if (strcmp(arg, "-W2,-Rcond_elim") == 0) { /* * Elimination and expansion of conditionals; * gcc has no direct equivalent. */ break; } if (strcmp(arg, "-Wd,-xsafe=unboundsym") == 0) { /* * Prevents optimizing away checks for * unbound weak symbol addresses. gcc does * not do this, so it's not needed. */ break; } if (strncmp(arg, "-Wc,-xcode=", 11) == 0) { xlate(ctx->i_ae, arg + 11, xcode_tbl); if (strncmp(arg + 11, "pic", 3) == 0) pic = 1; break; } if (strncmp(arg, "-Wc,-Qiselect", 13) == 0) { /* * Prevents insertion of register symbols. * gcc doesn't do this, so ignore it. */ break; } if (strcmp(arg, "-Wc,-Qassembler-ounrefsym=0") == 0) { /* * Prevents optimizing away of static variables. * gcc does not do this, so it's not needed. */ break; } #if defined(__x86) if (strcmp(arg, "-Wu,-no_got_reloc") == 0) { newae(ctx->i_ae, "-fno-jump-tables"); newae(ctx->i_ae, "-fno-constant-pools"); break; } if (strcmp(arg, "-Wu,-xmodel=kernel") == 0) { newae(ctx->i_ae, "-ffreestanding"); newae(ctx->i_ae, "-mno-red-zone"); model = "-mcmodel=kernel"; nolibc = 1; break; } if (strcmp(arg, "-Wu,-save_args") == 0) { newae(ctx->i_ae, "-msave-args"); break; } #endif /* __x86 */ error(arg); break; case 'X': if (strcmp(arg, "-Xa") == 0 || strcmp(arg, "-Xt") == 0) { Xamode(ctx->i_ae); break; } if (strcmp(arg, "-Xc") == 0) { Xcmode(ctx->i_ae); break; } if (strcmp(arg, "-Xs") == 0) { Xsmode(ctx->i_ae); break; } error(arg); break; case 'x': if (arglen == 1) error(arg); switch (arg[2]) { #if defined(__x86) case '3': if (strcmp(arg, "-x386") == 0) { newae(ctx->i_ae, "-march=i386"); break; } error(arg); break; case '4': if (strcmp(arg, "-x486") == 0) { newae(ctx->i_ae, "-march=i486"); break; } error(arg); break; #endif /* __x86 */ case 'a': if (strncmp(arg, "-xarch=", 7) == 0) { xlate(ctx->i_ae, arg + 7, xarch_tbl); break; } error(arg); break; case 'b': if (strncmp(arg, "-xbuiltin=", 10) == 0) { if (strcmp(arg + 10, "%all")) newae(ctx->i_ae, "-fbuiltin"); break; } error(arg); break; case 'C': /* Accept C++ style comments -- ignore */ if (strcmp(arg, "-xCC") == 0) break; error(arg); break; case 'c': if (strncmp(arg, "-xc99=%all", 10) == 0) { newae(ctx->i_ae, "-std=gnu99"); break; } if (strncmp(arg, "-xc99=%none", 11) == 0) { newae(ctx->i_ae, "-std=gnu89"); break; } if (strncmp(arg, "-xchip=", 7) == 0) { xlate(ctx->i_ae, arg + 7, xchip_tbl); break; } if (strncmp(arg, "-xcode=", 7) == 0) { xlate(ctx->i_ae, arg + 7, xcode_tbl); if (strncmp(arg + 7, "pic", 3) == 0) pic = 1; break; } if (strncmp(arg, "-xcache=", 8) == 0) break; if (strncmp(arg, "-xcrossfile", 11) == 0) break; error(arg); break; case 'd': if (strcmp(arg, "-xdepend") == 0) break; if (strncmp(arg, "-xdebugformat=", 14) == 0) break; error(arg); break; case 'F': /* compile for mapfile reordering -- ignore */ if (strcmp(arg, "-xF") == 0) break; error(arg); break; case 'i': if (strncmp(arg, "-xinline", 8) == 0) /* No inlining; ignore */ break; if (strcmp(arg, "-xildon") == 0 || strcmp(arg, "-xildoff") == 0) /* No incremental linking; ignore */ break; error(arg); break; #if defined(__x86) case 'm': if (strcmp(arg, "-xmodel=kernel") == 0) { newae(ctx->i_ae, "-ffreestanding"); newae(ctx->i_ae, "-mno-red-zone"); model = "-mcmodel=kernel"; nolibc = 1; break; } error(arg); break; #endif /* __x86 */ case 'M': if (strcmp(arg, "-xM") == 0) { newae(ctx->i_ae, "-M"); break; } if (strcmp(arg, "-xM1") == 0) { newae(ctx->i_ae, "-MM"); break; } error(arg); break; case 'n': if (strcmp(arg, "-xnolib") == 0) { nolibc = 1; break; } error(arg); break; case 'O': if (strncmp(arg, "-xO", 3) == 0) { size_t len = strlen(arg); char *s; int c = *(arg + 3); int level; if (len != 4 || !isdigit(c)) error(arg); if ((s = malloc(len)) == NULL) nomem(); level = atoi(arg + 3); if (level > 5) error(arg); if (level >= 2) { /* * For gcc-3.4.x at -O2 we * need to disable optimizations * that break ON. */ optim_disable(ctx->i_ae, level); /* * limit -xO3 to -O2 as well. */ level = 2; } (void) snprintf(s, len, "-O%d", level); newae(ctx->i_ae, s); free(s); break; } error(arg); break; case 'p': if (strcmp(arg, "-xpentium") == 0) { newae(ctx->i_ae, "-march=pentium"); break; } if (strcmp(arg, "-xpg") == 0) { newae(ctx->i_ae, "-pg"); break; } error(arg); break; case 'r': if (strncmp(arg, "-xregs=", 7) == 0) { xlate(ctx->i_ae, arg + 7, xregs_tbl); break; } error(arg); break; case 's': if (strcmp(arg, "-xs") == 0 || strcmp(arg, "-xspace") == 0 || strcmp(arg, "-xstrconst") == 0) break; error(arg); break; case 't': if (strcmp(arg, "-xtransition") == 0) { newae(ctx->i_ae, "-Wtransition"); break; } if (strcmp(arg, "-xtrigraphs=yes") == 0) { newae(ctx->i_ae, "-trigraphs"); break; } if (strcmp(arg, "-xtrigraphs=no") == 0) { newae(ctx->i_ae, "-notrigraphs"); break; } if (strncmp(arg, "-xtarget=", 9) == 0) { xlate(ctx->i_ae, arg + 9, xtarget_tbl); break; } error(arg); break; case 'e': case 'h': case 'l': default: error(arg); break; } break; case 'Y': if (arglen == 1) { if ((arg = *++ctx->i_oldargv) == NULL || *arg == '\0') error("-Y"); ctx->i_oldargc--; arglen = strlen(arg + 1); } else { arg += 2; } /* Just ignore -YS,... for now */ if (strncmp(arg, "S,", 2) == 0) break; if (strncmp(arg, "l,", 2) == 0) { char *s = strdup(arg); s[0] = '-'; s[1] = 'B'; newae(ctx->i_ae, s); free(s); break; } if (strncmp(arg, "I,", 2) == 0) { char *s = strdup(arg); s[0] = '-'; s[1] = 'I'; newae(ctx->i_ae, "-nostdinc"); newae(ctx->i_ae, s); free(s); break; } error(arg); break; case 'Q': /* * We could map -Qy into -Wl,-Qy etc. */ default: error(arg); break; } } if (c_files > 1 && (ctx->i_flags & CW_F_SHADOW) && op != CW_O_PREPROCESS) { (void) fprintf(stderr, "%s: error: multiple source files are " "allowed only with -E or -P\n", progname); exit(2); } if (op == CW_O_LINK && (ctx->i_flags & CW_F_SHADOW)) exit(0); if (model && !pic) newae(ctx->i_ae, model); if (!nolibc) newae(ctx->i_ae, "-lc"); if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { newae(ctx->i_ae, "-o"); newae(ctx->i_ae, ctx->i_discard); } } static void do_cc(cw_ictx_t *ctx) { int in_output = 0, seen_o = 0; cw_op_t op = CW_O_LINK; if (ctx->i_flags & CW_F_PROG) { newae(ctx->i_ae, "-V"); return; } while (--ctx->i_oldargc > 0) { char *arg = *++ctx->i_oldargv; if (*arg != '-') { if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) { newae(ctx->i_ae, arg); } else { in_output = 0; newae(ctx->i_ae, ctx->i_discard); } continue; } switch (*(arg + 1)) { case '_': if (strcmp(arg, "-_noecho") == 0) { ctx->i_flags &= ~CW_F_ECHO; } else if (strncmp(arg, "-_cc=", 5) == 0 || strncmp(arg, "-_CC=", 5) == 0) { newae(ctx->i_ae, arg + 5); } else if (strncmp(arg, "-_gcc=", 6) != 0 && strncmp(arg, "-_g++=", 6) != 0) { (void) fprintf(stderr, "%s: invalid argument '%s'\n", progname, arg); exit(2); } break; case 'V': ctx->i_flags &= ~CW_F_ECHO; newae(ctx->i_ae, arg); break; case 'o': seen_o = 1; if (strlen(arg) == 2) { in_output = 1; newae(ctx->i_ae, arg); } else if (ctx->i_flags & CW_F_SHADOW) { newae(ctx->i_ae, "-o"); newae(ctx->i_ae, ctx->i_discard); } else { newae(ctx->i_ae, arg); } break; case 'c': case 'S': op = CW_O_COMPILE; newae(ctx->i_ae, arg); break; case 'E': case 'P': op = CW_O_PREPROCESS; /*FALLTHROUGH*/ default: newae(ctx->i_ae, arg); } } if ((op == CW_O_LINK || op == CW_O_PREPROCESS) && (ctx->i_flags & CW_F_SHADOW)) exit(0); if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { newae(ctx->i_ae, "-o"); newae(ctx->i_ae, ctx->i_discard); } } static void prepctx(cw_ictx_t *ctx) { const char *dir, *cmd; char *program; size_t len; dir = dirs[CIDX(CC(ctx), ctx->i_flags)]; cmd = cmds[CIDX(CC(ctx), ctx->i_flags)]; len = strlen(dir) + strlen(cmd) + 2; if ((program = malloc(len)) == NULL) nomem(); (void) snprintf(program, len, "%s/%s", dir, cmd); newae(ctx->i_ae, program); if (ctx->i_flags & CW_F_PROG) { (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ? "shadow" : "primary", program); (void) fflush(stdout); } if (!(ctx->i_flags & CW_F_XLATE)) return; switch (CC(ctx)) { case CW_C_CC: do_cc(ctx); break; case CW_C_GCC: do_gcc(ctx); break; } } static int invoke(cw_ictx_t *ctx) { char **newargv; int ac; struct ae *a; if ((newargv = calloc(sizeof (*newargv), ctx->i_ae->ael_argc + 1)) == NULL) nomem(); if (ctx->i_flags & CW_F_ECHO) (void) fprintf(stderr, "+ "); for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) { newargv[ac] = a->ae_arg; if (ctx->i_flags & CW_F_ECHO) (void) fprintf(stderr, "%s ", a->ae_arg); if (a == ctx->i_ae->ael_tail) break; } if (ctx->i_flags & CW_F_ECHO) { (void) fprintf(stderr, "\n"); (void) fflush(stderr); } if (!(ctx->i_flags & CW_F_EXEC)) return (0); /* * We must fix up the environment here so that the * dependency files are not trampled by the shadow compiler. */ if ((ctx->i_flags & CW_F_SHADOW) && (unsetenv("SUNPRO_DEPENDENCIES") != 0 || unsetenv("DEPENDENCIES_OUTPUT") != 0)) { (void) fprintf(stderr, "error: environment setup failed: %s\n", strerror(errno)); return (-1); } (void) execv(newargv[0], newargv); cw_perror("couldn't run %s", newargv[0]); return (-1); } static int reap(cw_ictx_t *ctx) { int status, ret = 0; char buf[1024]; struct stat s; do { (void) waitpid(ctx->i_pid, &status, 0); if (status != 0) { if (WIFSIGNALED(status)) { ret = -WTERMSIG(status); break; } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); break; } } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); (void) unlink(ctx->i_discard); if (stat(ctx->i_stderr, &s) < 0) { cw_perror("stat failed on child cleanup"); return (-1); } if (s.st_size != 0) { FILE *f; if ((f = fopen(ctx->i_stderr, "r")) != NULL) { while (fgets(buf, sizeof (buf), f)) (void) fprintf(stderr, "%s", buf); (void) fflush(stderr); (void) fclose(f); } } (void) unlink(ctx->i_stderr); free(ctx->i_stderr); /* * cc returns an error code when given -V; we want that to succeed. */ if (ctx->i_flags & CW_F_PROG) return (0); return (ret); } static int exec_ctx(cw_ictx_t *ctx, int block) { char *file; /* * To avoid offending cc's sensibilities, the name of its output * file must end in '.o'. */ if ((file = tempnam(NULL, ".cw")) == NULL) { nomem(); return (-1); } (void) strlcpy(ctx->i_discard, file, MAXPATHLEN); (void) strlcat(ctx->i_discard, ".o", MAXPATHLEN); free(file); if ((ctx->i_stderr = tempnam(NULL, ".cw")) == NULL) { nomem(); return (-1); } if ((ctx->i_pid = fork()) == 0) { int fd; (void) fclose(stderr); if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) { cw_perror("open failed for standard error"); exit(1); } if (dup2(fd, 2) < 0) { cw_perror("dup2 failed for standard error"); exit(1); } if (fd != 2) (void) close(fd); if (freopen("/dev/fd/2", "w", stderr) == NULL) { cw_perror("freopen failed for /dev/fd/2"); exit(1); } prepctx(ctx); exit(invoke(ctx)); } if (ctx->i_pid < 0) { cw_perror("fork failed"); return (1); } if (block) return (reap(ctx)); return (0); } int main(int argc, char **argv) { cw_ictx_t *ctx = newictx(); cw_ictx_t *ctx_shadow = newictx(); const char *dir; char cc_buf[MAXPATHLEN], gcc_buf[MAXPATHLEN]; int do_serial, do_shadow; int ret = 0; if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else progname++; if (ctx == NULL || ctx_shadow == NULL) nomem(); ctx->i_flags = CW_F_ECHO|CW_F_XLATE; /* * Figure out where to get our tools from. This depends on * the environment variables set at run time. */ if ((dir = getenv("SPRO_VROOT")) != NULL) { (void) snprintf(cc_buf, MAXPATHLEN, "%s/bin", dir); } else if ((dir = getenv("SPRO_ROOT")) != NULL) { (void) snprintf(cc_buf, MAXPATHLEN, "%s/SS11/bin", dir); } else if ((dir = getenv("BUILD_TOOLS")) != NULL) { (void) snprintf(cc_buf, MAXPATHLEN, "%s/SUNWspro/SS11/bin", dir); } if (dir != NULL) { dirs[CIDX(CW_C_CC, 0)] = (const char *)cc_buf; dirs[CIDX(CW_C_CC, CW_F_CXX)] = (const char *)cc_buf; } if ((dir = getenv("GNU_ROOT")) != NULL) { (void) snprintf(gcc_buf, MAXPATHLEN, "%s/bin", dir); dirs[CIDX(CW_C_GCC, 0)] = (const char *)gcc_buf; dirs[CIDX(CW_C_GCC, CW_F_CXX)] = (const char *)gcc_buf; } if ((dir = getenv("CW_CC_DIR")) != NULL) dirs[CIDX(CW_C_CC, 0)] = dir; if ((dir = getenv("CW_CPLUSPLUS_DIR")) != NULL) dirs[CIDX(CW_C_CC, CW_F_CXX)] = dir; if ((dir = getenv("CW_GCC_DIR")) != NULL) dirs[CIDX(CW_C_GCC, 0)] = dir; if ((dir = getenv("CW_GPLUSPLUS_DIR")) != NULL) dirs[CIDX(CW_C_GCC, CW_F_CXX)] = dir; do_shadow = (getenv("CW_NO_SHADOW") ? 0 : 1); do_serial = (getenv("CW_SHADOW_SERIAL") ? 1 : 0); if (getenv("CW_NO_EXEC") == NULL) ctx->i_flags |= CW_F_EXEC; /* * The first argument must be one of "-_cc", "-_gcc", "-_CC", or "-_g++" */ if (argc == 1) usage(); argc--; argv++; if (strcmp(argv[0], "-_cc") == 0) { ctx->i_compiler = CW_C_CC; } else if (strcmp(argv[0], "-_gcc") == 0) { ctx->i_compiler = CW_C_GCC; } else if (strcmp(argv[0], "-_CC") == 0) { ctx->i_compiler = CW_C_CC; ctx->i_flags |= CW_F_CXX; } else if (strcmp(argv[0], "-_g++") == 0) { ctx->i_compiler = CW_C_GCC; ctx->i_flags |= CW_F_CXX; } else { /* assume "-_gcc" by default */ argc++; argv--; ctx->i_compiler = CW_C_GCC; } /* * -_compiler - tell us the path to the primary compiler only */ if (argc > 1 && strcmp(argv[1], "-_compiler") == 0) { ctx->i_flags &= ~CW_F_XLATE; prepctx(ctx); (void) printf("%s\n", ctx->i_ae->ael_head->ae_arg); return (0); } /* * -_versions - tell us the cw version, paths to all compilers, and * ask each for its version if we know how. */ if (argc > 1 && strcmp(argv[1], "-_versions") == 0) { (void) printf("%s", "cw version %I%"); if (!do_shadow) (void) printf(" (SHADOW MODE DISABLED)"); (void) printf("\n"); (void) fflush(stdout); ctx->i_flags &= ~CW_F_ECHO; ctx->i_flags |= CW_F_PROG|CW_F_EXEC; argc--; argv++; do_serial = 1; } ctx->i_oldargc = argc; ctx->i_oldargv = argv; ret |= exec_ctx(ctx, do_serial); if (do_shadow) { (void) memcpy(ctx_shadow, ctx, sizeof (cw_ictx_t)); ctx_shadow->i_flags |= CW_F_SHADOW; ret |= exec_ctx(ctx_shadow, 1); } if (!do_serial) ret |= reap(ctx); return (ret); }