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
nomem(void)410 nomem(void)
411 {
412 errx(1, "out of memory");
413 }
414
415 static void
newae(struct aelist * ael,const char * arg)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 *
newictx(void)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
error(const char * arg)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
warnings(struct aelist * h)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
optim_disable(struct aelist * h,int level)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
Xsmode(struct aelist * h)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
usage(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
xlate_xtb(struct aelist * h,const char * xarg)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
xlate(struct aelist * h,const char * xarg,const char ** table)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 *
discard_file_name(cw_ictx_t * ctx,const char * path)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
is_source_file(const char * path)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
do_gcc(cw_ictx_t * ctx)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
do_smatch(cw_ictx_t * ctx)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
do_cc(cw_ictx_t * ctx)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
prepctx(cw_ictx_t * ctx)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
invoke(cw_ictx_t * ctx)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
reap(cw_ictx_t * ctx)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
exec_ctx(cw_ictx_t * ctx,int block)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
parse_compiler(const char * spec,cw_compiler_t * compiler)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
cleanup(cw_ictx_t * ctx)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
main(int argc,char ** argv)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