/* * 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 2018, Richard Lowe. */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * 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. * */ /* If you modify this file, you must increment CW_VERSION */ #define CW_VERSION "3.0" /* * -# 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 * -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 * -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 * -p Compile for profiling with prof * -Q[y|n] Emit/don't emit identification info to output file * -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) * -Xs Compile assuming (pre-ANSI) K & R C style code * -Xt Compile assuming K & R conformance, allow ANSI C * -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 * -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 or unused section * elimination * -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 * -xmaxopt=[off,1,2,3,4,5] maximum optimization level allowed on #pragma opt * -xO Generate optimized code (n={1|2|3|4|5}) * -xP Print prototypes for function definitions * -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 * -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 * -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 * -norunpath ignore * -O -O1 (Check the man page to be certain) * -o pass-thru * -P -E -o filename.i (or error) * -p pass-thru * -Q[y|n] error * -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 * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone * -Wu,-save_args -msave-args * -w pass-thru * -Xa -std=iso9899:199409 or -ansi * -Xt error * -Xs -traditional -std=c89 * -xarch= table * -xbuiltin[=] -fbuiltin (-fno-builtin otherwise) * -xCC ignore * -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 * -xmaxopt=[...] error * -xO -O * -xP 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 * -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_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 enum { GNU, SUN } compiler_style_t; typedef struct { char *c_name; char *c_path; compiler_style_t c_style; } cw_compiler_t; typedef struct cw_ictx { struct cw_ictx *i_next; 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; /* * Status values to indicate which Studio compiler and associated * flags are being used. */ #define M32 0x01 /* -m32 - only on Studio 12 */ #define M64 0x02 /* -m64 - only on Studio 12 */ #define SS11 0x100 /* Studio 11 */ #define SS12 0x200 /* Studio 12 */ #define TRANS_ENTRY 5 /* * Translation table definition for the -xarch= flag. The "x_arg" * value is translated into the appropriate gcc flags according * to the values in x_trans[n]. The x_flags indicates what compiler * is being used and what flags have been set via the use of * "x_arg". */ typedef struct xarch_table { char *x_arg; int x_flags; char *x_trans[TRANS_ENTRY]; } xarch_table_t; /* * The translation table for the -xarch= flag used in the Studio compilers. */ static const xarch_table_t xtbl[] = { #if defined(__x86) { "generic", SS11, {NULL} }, { "generic64", (SS11|M64), { "-m64", "-mtune=opteron" } }, { "amd64", (SS11|M64), { "-m64", "-mtune=opteron" } }, { "386", SS11, { "-march=i386" } }, { "pentium_pro", SS11, { "-march=pentiumpro" } }, { "sse", SS11, { "-msse", "-mfpmath=sse" } }, { "sse2", SS11, { "-msse2", "-mfpmath=sse" } }, #elif defined(__sparc) { "generic", (SS11|M32), { "-m32", "-mcpu=v8" } }, { "generic64", (SS11|M64), { "-m64", "-mcpu=v9" } }, { "v8", (SS11|M32), { "-m32", "-mcpu=v8", "-mno-v8plus" } }, { "v8plus", (SS11|M32), { "-m32", "-mcpu=v9", "-mv8plus" } }, { "v8plusa", (SS11|M32), { "-m32", "-mcpu=ultrasparc", "-mv8plus", "-mvis" } }, { "v8plusb", (SS11|M32), { "-m32", "-mcpu=ultrasparc3", "-mv8plus", "-mvis" } }, { "v9", (SS11|M64), { "-m64", "-mcpu=v9" } }, { "v9a", (SS11|M64), { "-m64", "-mcpu=ultrasparc", "-mvis" } }, { "v9b", (SS11|M64), { "-m64", "-mcpu=ultrasparc3", "-mvis" } }, { "sparc", SS12, { "-mcpu=v9", "-mv8plus" } }, { "sparcvis", SS12, { "-mcpu=ultrasparc", "-mvis" } }, { "sparcvis2", SS12, { "-mcpu=ultrasparc3", "-mvis" } } #endif }; static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t); 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) { errx(1, "out of memory"); } 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) { errx(2, "error: mapping failed at or near arg '%s'", arg); } /* * Add the current favourite set of warnings to the gcc invocation. */ static void warnings(struct aelist *h) { static int warningsonce; if (warningsonce++) return; /* * Enable as many warnings as exist, then disable those that we never * ever want. */ newae(h, "-Wall"); newae(h, "-Wextra"); } 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 __unused *h) { } static void Xsmode(struct aelist *h) { static int xsonce; if (xsonce++) return; newae(h, "-traditional"); newae(h, "-traditional-cpp"); } static void usage() { extern char *__progname; (void) fprintf(stderr, "usage: %s [-C] [--versions] --primary " "[--shadow ]... -- cflags...\n", __progname); (void) fprintf(stderr, "compilers take the form: name,path,style\n" " - name: a unique name usable in flag specifiers\n" " - path: path to the compiler binary\n" " - style: the style of flags expected: either sun or gnu\n"); exit(2); } static int xlate_xtb(struct aelist *h, const char *xarg) { int i, j; for (i = 0; i < xtbl_size; i++) { if (strcmp(xtbl[i].x_arg, xarg) == 0) break; } /* * At the end of the table and so no matching "arg" entry * found and so this must be a bad -xarch= flag. */ if (i == xtbl_size) error(xarg); for (j = 0; j < TRANS_ENTRY; j++) { if (xtbl[i].x_trans[j] != NULL) newae(h, xtbl[i].x_trans[j]); else break; } return (xtbl[i].x_flags); } 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; char *nameflag; int mflag = 0; 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, "-fdiagnostics-show-option"); 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"); if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) nomem(); /* * 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, "-_g++=", 6) == 0) { newae(ctx->i_ae, strchr(arg, '=') + 1); continue; } 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, "-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) { mflag |= xlate_xtb(ctx->i_ae, "v8"); xlate(ctx->i_ae, "super", xchip_tbl); continue; } #endif /* __sparc */ } switch ((c = arg[1])) { case '_': if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || (strncmp(arg, "-_gcc=", 6) == 0) || (strncmp(arg, "-_gnu=", 6) == 0)) { newae(ctx->i_ae, strchr(arg, '=') + 1); } 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 '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; } if (strcmp(arg, "-m64") == 0) { newae(ctx->i_ae, "-m64"); #if defined(__x86) newae(ctx->i_ae, "-mtune=opteron"); #endif mflag |= M64; break; } if (strcmp(arg, "-m32") == 0) { newae(ctx->i_ae, "-m32"); mflag |= M32; 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 '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 '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,-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, "-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,-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, "-Xs") == 0) { Xsmode(ctx->i_ae); break; } error(arg); break; case 'x': if (arglen == 1) error(arg); switch (arg[2]) { case 'a': if (strncmp(arg, "-xarch=", 7) == 0) { mflag |= xlate_xtb(ctx->i_ae, arg + 7); 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, "-xcrossfile", 11) == 0) break; error(arg); break; case 'd': if (strncmp(arg, "-xdebugformat=", 14) == 0) break; error(arg); break; case 'F': /* * Compile for mapfile reordering, or unused * section elimination, syntax can be -xF or * more complex, like -xF=%all -- ignore. */ if (strncmp(arg, "-xF", 3) == 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 'O': if (strncmp(arg, "-xO", 3) == 0) { size_t len = strlen(arg); char *s = NULL; int c = *(arg + 3); int level; if (len != 4 || !isdigit(c)) error(arg); 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; } if (asprintf(&s, "-O%d", level) == -1) nomem(); newae(ctx->i_ae, s); free(s); 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 (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; } } free(nameflag); if (c_files > 1 && (ctx->i_flags & CW_F_SHADOW) && op != CW_O_PREPROCESS) { errx(2, "multiple source files are " "allowed only with -E or -P"); } /* * Make sure that we do not have any unintended interactions between * the xarch options passed in and the version of the Studio compiler * used. */ if ((mflag & (SS11|SS12)) == (SS11|SS12)) { errx(2, "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n"); } switch (mflag) { case 0: /* FALLTHROUGH */ case M32: #if defined(__sparc) /* * Only -m32 is defined and so put in the missing xarch * translation. */ newae(ctx->i_ae, "-mcpu=v8"); newae(ctx->i_ae, "-mno-v8plus"); #endif break; case M64: #if defined(__sparc) /* * Only -m64 is defined and so put in the missing xarch * translation. */ newae(ctx->i_ae, "-mcpu=v9"); #endif break; case SS12: #if defined(__sparc) /* no -m32/-m64 flag used - this is an error for sparc builds */ (void) fprintf(stderr, "No -m32/-m64 flag defined\n"); exit(2); #endif break; case SS11: /* FALLTHROUGH */ case (SS11|M32): case (SS11|M64): break; case (SS12|M32): #if defined(__sparc) /* * Need to add in further 32 bit options because with SS12 * the xarch=sparcvis option can be applied to 32 or 64 * bit, and so the translatation table (xtbl) cannot handle * that. */ newae(ctx->i_ae, "-mv8plus"); #endif break; case (SS12|M64): break; default: (void) fprintf(stderr, "Incompatible -xarch= and/or -m32/-m64 options used.\n"); exit(2); } if ((op == CW_O_LINK || op == CW_O_PREPROCESS) && (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; char *nameflag; if (ctx->i_flags & CW_F_PROG) { newae(ctx->i_ae, "-V"); return; } if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) nomem(); while (--ctx->i_oldargc > 0) { char *arg = *++ctx->i_oldargv; if (strncmp(arg, "-_CC=", 5) == 0) { newae(ctx->i_ae, strchr(arg, '=') + 1); continue; } 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 ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || (strncmp(arg, "-_cc=", 5) == 0) || (strncmp(arg, "-_sun=", 6) == 0)) { newae(ctx->i_ae, strchr(arg, '=') + 1); } 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': if (strlen(arg) == 2) op = CW_O_COMPILE; newae(ctx->i_ae, arg); break; case 'E': case 'P': if (strlen(arg) == 2) op = CW_O_PREPROCESS; /*FALLTHROUGH*/ default: newae(ctx->i_ae, arg); } } free(nameflag); 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) { newae(ctx->i_ae, ctx->i_compiler->c_path); if (ctx->i_flags & CW_F_PROG) { (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ? "shadow" : "primary", ctx->i_compiler->c_path); (void) fflush(stdout); } if (!(ctx->i_flags & CW_F_XLATE)) return; switch (ctx->i_compiler->c_style) { case SUN: do_cc(ctx); break; case GNU: 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. Also take care of GCC * environment variables that will throw off gcc. This assumes a primary * gcc. */ if ((ctx->i_flags & CW_F_SHADOW) && (unsetenv("SUNPRO_DEPENDENCIES") != 0 || unsetenv("DEPENDENCIES_OUTPUT") != 0 || unsetenv("GCC_ROOT") != 0)) { (void) fprintf(stderr, "error: environment setup failed: %s\n", strerror(errno)); return (-1); } (void) execv(newargv[0], newargv); warn("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; /* * Only wait for one specific child. */ if (ctx->i_pid <= 0) return (-1); do { if (waitpid(ctx->i_pid, &status, 0) < 0) { warn("cannot reap child"); return (-1); } 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) { warn("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) { err(1, "open failed for standard error"); } if (dup2(fd, 2) < 0) { err(1, "dup2 failed for standard error"); } if (fd != 2) (void) close(fd); if (freopen("/dev/fd/2", "w", stderr) == NULL) { err(1, "freopen failed for /dev/fd/2"); } prepctx(ctx); exit(invoke(ctx)); } if (ctx->i_pid < 0) { err(1, "fork failed"); } if (block) return (reap(ctx)); return (0); } static void parse_compiler(const char *spec, cw_compiler_t *compiler) { char *tspec, *token; if ((tspec = strdup(spec)) == NULL) nomem(); if ((token = strsep(&tspec, ",")) == NULL) errx(1, "Compiler is missing a name: %s", spec); compiler->c_name = token; if ((token = strsep(&tspec, ",")) == NULL) errx(1, "Compiler is missing a path: %s", spec); compiler->c_path = token; if ((token = strsep(&tspec, ",")) == NULL) errx(1, "Compiler is missing a style: %s", spec); if ((strcasecmp(token, "gnu") == 0) || (strcasecmp(token, "gcc") == 0)) compiler->c_style = GNU; else if ((strcasecmp(token, "sun") == 0) || (strcasecmp(token, "cc") == 0)) compiler->c_style = SUN; else errx(1, "unknown compiler style: %s", token); if (tspec != NULL) errx(1, "Excess tokens in compiler: %s", spec); } int main(int argc, char **argv) { int ch; cw_compiler_t primary = { NULL, NULL, 0 }; cw_compiler_t shadows[10]; int nshadows = 0; int ret = 0; boolean_t do_serial = B_FALSE; boolean_t do_exec = B_FALSE; boolean_t vflg = B_FALSE; boolean_t Cflg = B_FALSE; boolean_t cflg = B_FALSE; boolean_t nflg = B_FALSE; cw_ictx_t *main_ctx; static struct option longopts[] = { { "compiler", no_argument, NULL, 'c' }, { "noecho", no_argument, NULL, 'n' }, { "primary", required_argument, NULL, 'p' }, { "shadow", required_argument, NULL, 's' }, { "versions", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 }, }; if ((main_ctx = newictx()) == NULL) nomem(); while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) { switch (ch) { case 'c': cflg = B_TRUE; break; case 'C': Cflg = B_TRUE; break; case 'n': nflg = B_TRUE; break; case 'p': if (primary.c_path != NULL) { warnx("Only one primary compiler may " "be specified"); usage(); } parse_compiler(optarg, &primary); break; case 's': if (nshadows >= 10) errx(1, "May only use 10 shadows at " "the moment"); parse_compiler(optarg, &shadows[nshadows]); nshadows++; break; case 'v': vflg = B_TRUE; break; default: (void) fprintf(stderr, "Did you forget '--'?\n"); usage(); } } if (primary.c_path == NULL) { warnx("A primary compiler must be specified"); usage(); } do_serial = (getenv("CW_SHADOW_SERIAL") == NULL) ? B_FALSE : B_TRUE; do_exec = (getenv("CW_NO_EXEC") == NULL) ? B_TRUE : B_FALSE; /* Leave room for argv[0] */ argc -= (optind - 1); argv += (optind - 1); main_ctx->i_oldargc = argc; main_ctx->i_oldargv = argv; main_ctx->i_flags = CW_F_XLATE; if (nflg == 0) main_ctx->i_flags |= CW_F_ECHO; if (do_exec) main_ctx->i_flags |= CW_F_EXEC; if (Cflg) main_ctx->i_flags |= CW_F_CXX; main_ctx->i_compiler = &primary; if (cflg) { (void) fputs(primary.c_path, stdout); } if (vflg) { (void) printf("cw version %s\n", CW_VERSION); (void) fflush(stdout); main_ctx->i_flags &= ~CW_F_ECHO; main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC; do_serial = 1; } ret |= exec_ctx(main_ctx, do_serial); for (int i = 0; i < nshadows; i++) { int r; cw_ictx_t *shadow_ctx; if ((shadow_ctx = newictx()) == NULL) nomem(); memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t)); shadow_ctx->i_flags |= CW_F_SHADOW; shadow_ctx->i_compiler = &shadows[i]; r = exec_ctx(shadow_ctx, do_serial); if (r == 0) { shadow_ctx->i_next = main_ctx->i_next; main_ctx->i_next = shadow_ctx; } ret |= r; } if (!do_serial) { cw_ictx_t *next = main_ctx; while (next != NULL) { cw_ictx_t *toreap = next; next = next->i_next; ret |= reap(toreap); } } return (ret); }