Print this page
9899 cw(1onbld) should shadow more compilation

@@ -260,10 +260,11 @@
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/utsname.h>

@@ -309,11 +310,11 @@
         struct aelist   *i_ae;
         uint32_t        i_flags;
         int             i_oldargc;
         char            **i_oldargv;
         pid_t           i_pid;
-        char            i_discard[MAXPATHLEN];
+        char            *i_tmpdir;
         char            *i_stderr;
 } cw_ictx_t;
 
 /*
  * Status values to indicate which Studio compiler and associated

@@ -554,10 +555,47 @@
                 newae(h, *table);
                 table++;
         }
 }
 
+/*
+ * The compiler wants the output file to end in appropriate extension.  If
+ * we're generating a name from whole cloth (path == NULL), we assume that
+ * extension to be .o, otherwise we match the extension of the caller.
+ */
+static char *
+discard_file_name(cw_ictx_t *ctx, const char *path)
+{
+        char *ret, *ext;
+        char tmpl[] = "cwXXXXXX";
+
+        if (path == NULL) {
+                ext = ".o";
+        } else {
+                ext = strrchr(path, '.');
+        }
+
+        /*
+         * We need absolute control over where the temporary file goes, since
+         * we rely on it for cleanup so tempnam(3C) and tmpnam(3C) are
+         * inappropriate (they use TMPDIR, preferentially).
+         *
+         * mkstemp(3C) doesn't actually help us, since the temporary file
+         * isn't used by us, only its name.
+         */
+        if (mktemp(tmpl) == NULL)
+                nomem();
+
+        (void) asprintf(&ret, "%s/%s%s", ctx->i_tmpdir, tmpl,
+            (ext != NULL) ? ext : "");
+
+        if (ret == NULL)
+                nomem();
+
+        return (ret);
+}
+
 static void
 do_gcc(cw_ictx_t *ctx)
 {
         int c;
         int nolibc = 0;

@@ -631,14 +669,15 @@
                          * 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
+                        if ((ctx->i_flags & CW_F_SHADOW) && in_output) {
+                                newae(ctx->i_ae, discard_file_name(ctx, arg));
+                        } else {
                                 newae(ctx->i_ae, arg);
+                        }
                         in_output = 0;
                         continue;
                 }
 
                 if (ctx->i_flags & CW_F_CXX) {

@@ -750,11 +789,11 @@
                         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);
+                                newae(ctx->i_ae, discard_file_name(ctx, arg));
                         } else {
                                 newae(ctx->i_ae, arg);
                         }
                         break;
                 case 'D':

@@ -1188,12 +1227,20 @@
                 }
         }
 
         free(nameflag);
 
-        if (c_files > 1 && (ctx->i_flags & CW_F_SHADOW) &&
-            op != CW_O_PREPROCESS) {
+        /*
+         * When compiling multiple source files in a single invocation some
+         * compilers output objects into the current directory with
+         * predictable and conventional names.
+         *
+         * We prevent any attempt to compile multiple files at once so that
+         * any such objects created by a shadow can't escape into a later
+         * link-edit.
+         */
+        if (c_files > 1 && op != CW_O_PREPROCESS) {
                 errx(2, "multiple source files are "
                     "allowed only with -E or -P");
         }
 
         /*

@@ -1257,21 +1304,24 @@
                 (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))
+        if (ctx->i_flags & CW_F_SHADOW) {
+                if (op == CW_O_PREPROCESS)
+                        exit(0);
+                else if (op == CW_O_LINK && c_files == 0)
                 exit(0);
+        }
 
         if (model != NULL)
                 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);
+                newae(ctx->i_ae, discard_file_name(ctx, NULL));
         }
 }
 
 static void
 do_smatch(cw_ictx_t *ctx)

@@ -1300,11 +1350,11 @@
 }
 
 static void
 do_cc(cw_ictx_t *ctx)
 {
-        int in_output = 0, seen_o = 0;
+        int in_output = 0, seen_o = 0, c_files = 0;
         cw_op_t op = CW_O_LINK;
         char *nameflag;
 
         if (ctx->i_flags & CW_F_PROG) {
                 newae(ctx->i_ae, "-V");

@@ -1314,22 +1364,29 @@
         if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
                 nomem();
 
         while (--ctx->i_oldargc > 0) {
                 char *arg = *++ctx->i_oldargv;
+                size_t arglen = strlen(arg);
 
                 if (strncmp(arg, "-_CC=", 5) == 0) {
                         newae(ctx->i_ae, strchr(arg, '=') + 1);
                         continue;
                 }
 
                 if (*arg != '-') {
+                        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++;
+
                         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);
+                                newae(ctx->i_ae, discard_file_name(ctx, arg));
                         }
                         continue;
                 }
                 switch (*(arg + 1)) {
                 case '_':

@@ -1349,11 +1406,11 @@
                         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);
+                                newae(ctx->i_ae, discard_file_name(ctx, arg));
                         } else {
                                 newae(ctx->i_ae, arg);
                         }
                         break;
                 case 'c':

@@ -1372,17 +1429,26 @@
                 }
         }
 
         free(nameflag);
 
-        if ((op == CW_O_LINK || op == CW_O_PREPROCESS) &&
-            (ctx->i_flags & CW_F_SHADOW))
+        /* See the comment on this same code in do_gcc() */
+        if (c_files > 1 && op != CW_O_PREPROCESS) {
+                errx(2, "multiple source files are "
+                    "allowed only with -E or -P");
+        }
+
+        if (ctx->i_flags & CW_F_SHADOW) {
+                if (op == CW_O_PREPROCESS)
                 exit(0);
+                else if (op == CW_O_LINK && c_files == 0)
+                        exit(0);
+        }
 
         if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
                 newae(ctx->i_ae, "-o");
-                newae(ctx->i_ae, ctx->i_discard);
+                newae(ctx->i_ae, discard_file_name(ctx, NULL));
         }
 }
 
 static void
 prepctx(cw_ictx_t *ctx)

@@ -1489,12 +1555,10 @@
                                 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) {

@@ -1520,25 +1584,11 @@
 }
 
 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) {
+        if ((ctx->i_stderr = tempnam(ctx->i_tmpdir, "cw")) == NULL) {
                 nomem();
                 return (-1);
         }
 
         if ((ctx->i_pid = fork()) == 0) {

@@ -1605,10 +1655,52 @@
 
         if (tspec != NULL)
                 errx(1, "Excess tokens in compiler: %s", spec);
 }
 
+static void
+cleanup(cw_ictx_t *ctx)
+{
+        DIR *dirp;
+        struct dirent *dp;
+        char buf[MAXPATHLEN];
+
+        if ((dirp = opendir(ctx->i_tmpdir)) == NULL) {
+                if (errno != ENOENT) {
+                        err(1, "couldn't open temp directory: %s",
+                            ctx->i_tmpdir);
+                } else {
+                        return;
+                }
+        }
+
+        errno = 0;
+        while ((dp = readdir(dirp)) != NULL) {
+                (void) snprintf(buf, MAXPATHLEN, "%s/%s", ctx->i_tmpdir,
+                    dp->d_name);
+
+                if (strncmp(dp->d_name, ".", strlen(dp->d_name)) == 0 ||
+                    strncmp(dp->d_name, "..", strlen(dp->d_name)) == 0)
+                        continue;
+
+                if (unlink(buf) == -1)
+                        err(1, "failed to unlink temp file: %s", dp->d_name);
+                errno = 0;
+        }
+
+        if (errno != 0) {
+                err(1, "failed to read temporary directory: %s",
+                    ctx->i_tmpdir);
+        }
+
+        (void) closedir(dirp);
+        if (rmdir(ctx->i_tmpdir) != 0) {
+                err(1, "failed to unlink temporary directory: %s",
+                    ctx->i_tmpdir);
+        }
+}
+
 int
 main(int argc, char **argv)
 {
         int ch;
         cw_compiler_t primary = { NULL, NULL, 0 };

@@ -1619,10 +1711,11 @@
         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;
+        char *tmpdir;
 
         cw_ictx_t *main_ctx;
 
         static struct option longopts[] = {
                 { "compiler", no_argument, NULL, 'c' },

@@ -1706,20 +1799,30 @@
                 main_ctx->i_flags &= ~CW_F_ECHO;
                 main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC;
                 do_serial = 1;
         }
 
+        tmpdir = getenv("TMPDIR");
+        if (tmpdir == NULL)
+                tmpdir = "/tmp";
+
+        if (asprintf(&main_ctx->i_tmpdir, "%s/cw.XXXXXX", tmpdir) == -1)
+                nomem();
+
+        if ((main_ctx->i_tmpdir = mkdtemp(main_ctx->i_tmpdir)) == NULL)
+                errx(1, "failed to create temporary directory");
+
         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));
+                (void) 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);

@@ -1737,7 +1840,8 @@
                         next = next->i_next;
                         ret |= reap(toreap);
                 }
         }
 
+        cleanup(main_ctx);
         return (ret);
 }