--- old/usr/src/tools/cw/Makefile	2019-01-15 20:51:36.204617371 +0000
+++ new/usr/src/tools/cw/Makefile	2019-01-15 20:51:36.263882283 +0000
@@ -49,6 +49,7 @@
 $(__GNUC)LDFLAGS=	$(MAPFILE.NES:%=-Wl,-M%)
 
 $(ROOTONBLDMAN1ONBLDFILES) := FILEMODE=      644
+CSTD=	$(CSTD_GNU99)
 
 # Assume we don't have the install.bin available yet
 INS.file=	$(RM) $@; $(CP) $< $(@D); $(CHMOD) $(FILEMODE) $@
--- old/usr/src/tools/cw/cw.1onbld	2019-01-15 20:51:36.768994910 +0000
+++ new/usr/src/tools/cw/cw.1onbld	2019-01-15 20:51:36.785403603 +0000
@@ -137,11 +137,10 @@
 translation for compiler style) as follows:
 .Bl -enum
 .It
-If neither of
-.Fl c ,
-.Fl S
-appears in the argument list (that is, linking is attempted or only the
-pre-processor is invoked), the shadow compilers will not be invoked.
+If
+.Nm cw
+is invoked to link-edit without compilation (the input files are all objects),
+the shadow compiler is not invoked.
 .It
 If the
 .Fl o Ar filename
--- old/usr/src/tools/cw/cw.c	2019-01-15 20:51:37.499709822 +0000
+++ new/usr/src/tools/cw/cw.c	2019-01-15 20:51:37.522572417 +0000
@@ -262,6 +262,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #include <sys/param.h>
 #include <sys/stat.h>
@@ -311,7 +312,7 @@
 	int		i_oldargc;
 	char		**i_oldargv;
 	pid_t		i_pid;
-	char		i_discard[MAXPATHLEN];
+	char		*i_tmpdir;
 	char		*i_stderr;
 } cw_ictx_t;
 
@@ -556,6 +557,43 @@
 	}
 }
 
+/*
+ * 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)
 {
@@ -565,7 +603,7 @@
 	cw_op_t op = CW_O_LINK;
 	char *model = NULL;
 	char *nameflag;
-	int	mflag = 0;
+	int mflag = 0;
 
 	if (ctx->i_flags & CW_F_PROG) {
 		newae(ctx->i_ae, "--version");
@@ -633,10 +671,11 @@
 			 * 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;
 		}
@@ -752,7 +791,7 @@
 				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);
 			}
@@ -1190,8 +1229,16 @@
 
 	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");
 	}
@@ -1259,9 +1306,12 @@
 		exit(2);
 	}
 
-	if ((op == CW_O_LINK || op == CW_O_PREPROCESS) &&
-	    (ctx->i_flags & CW_F_SHADOW))
-		exit(0);
+	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);
@@ -1269,7 +1319,7 @@
 		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));
 	}
 }
 
@@ -1302,7 +1352,7 @@
 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;
 
@@ -1316,6 +1366,7 @@
 
 	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);
@@ -1323,11 +1374,17 @@
 		}
 
 		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;
 		}
@@ -1351,7 +1408,7 @@
 				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);
 			}
@@ -1374,13 +1431,22 @@
 
 	free(nameflag);
 
-	if ((op == CW_O_LINK || op == CW_O_PREPROCESS) &&
-	    (ctx->i_flags & CW_F_SHADOW))
-		exit(0);
+	/* 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));
 	}
 }
 
@@ -1491,8 +1557,6 @@
 		}
 	} 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);
@@ -1522,21 +1586,7 @@
 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);
 	}
@@ -1607,6 +1657,48 @@
 		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)
 {
@@ -1621,6 +1713,7 @@
 	boolean_t Cflg = B_FALSE;
 	boolean_t cflg = B_FALSE;
 	boolean_t nflg = B_FALSE;
+	char *tmpdir;
 
 	cw_ictx_t *main_ctx;
 
@@ -1708,6 +1801,16 @@
 		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++) {
@@ -1717,7 +1820,7 @@
 		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];
@@ -1739,5 +1842,6 @@
 		}
 	}
 
+	cleanup(main_ctx);
 	return (ret);
 }
