--- old/usr/src/tools/cw/cw.1onbld	2018-10-17 17:00:27.171196153 +0000
+++ new/usr/src/tools/cw/cw.1onbld	2018-10-17 17:00:27.183758746 +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	2018-10-17 17:00:35.179662687 +0000
+++ new/usr/src/tools/cw/cw.c	2018-10-17 17:00:35.196483423 +0000
@@ -308,7 +308,7 @@
 	int		i_oldargc;
 	char		**i_oldargv;
 	pid_t		i_pid;
-	char		i_discard[MAXPATHLEN];
+	char		*i_discard;
 	char		*i_stderr;
 } cw_ictx_t;
 
@@ -479,12 +479,6 @@
 	}
 }
 
-/* ARGSUSED */
-static void
-Xamode(struct aelist __unused *h)
-{
-}
-
 static void
 Xsmode(struct aelist *h)
 {
@@ -559,6 +553,35 @@
 	}
 }
 
+/*
+ * 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(const char *path)
+{
+	char *ret, *ext, *file;
+
+	if (path == NULL) {
+		ext = ".o";
+	} else {
+		ext = strrchr(path, '.');
+	}
+
+	if ((ret = calloc(MAXPATHLEN, sizeof (char))) == NULL)
+		nomem();
+
+	if ((file = tempnam(NULL, ".cw")) == NULL)
+		nomem();
+
+	(void) strlcpy(ret, file, MAXPATHLEN);
+	if (ext != NULL)
+		(void) strlcat(ret, ext, MAXPATHLEN);
+	free(file);
+	return (ret);
+}
+
 static void
 do_gcc(cw_ictx_t *ctx)
 {
@@ -568,7 +591,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");
@@ -636,10 +659,15 @@
 			 * output is always discarded for the secondary
 			 * compiler.
 			 */
-			if ((ctx->i_flags & CW_F_SHADOW) && in_output)
+			if ((ctx->i_flags & CW_F_SHADOW) && in_output) {
+				ctx->i_discard = discard_file_name(arg);
+
+				if (ctx->i_discard == NULL)
+					nomem();
 				newae(ctx->i_ae, ctx->i_discard);
-			else
+			} else {
 				newae(ctx->i_ae, arg);
+			}
 			in_output = 0;
 			continue;
 		}
@@ -755,6 +783,10 @@
 				newae(ctx->i_ae, arg);
 			} else if (ctx->i_flags & CW_F_SHADOW) {
 				newae(ctx->i_ae, "-o");
+				ctx->i_discard = discard_file_name(arg);
+				if (ctx->i_discard == NULL)
+					nomem();
+
 				newae(ctx->i_ae, ctx->i_discard);
 			} else {
 				newae(ctx->i_ae, arg);
@@ -996,7 +1028,6 @@
 		case 'X':
 			if (strcmp(arg, "-Xa") == 0 ||
 			    strcmp(arg, "-Xt") == 0) {
-				Xamode(ctx->i_ae);
 				break;
 			}
 			if (strcmp(arg, "-Xs") == 0) {
@@ -1194,8 +1225,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");
 	}
@@ -1263,15 +1302,22 @@
 		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);
 	if (!nolibc)
 		newae(ctx->i_ae, "-lc");
 	if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
+		ctx->i_discard = discard_file_name(NULL);
+
+		if (ctx->i_discard == NULL)
+			nomem();
 		newae(ctx->i_ae, "-o");
 		newae(ctx->i_ae, ctx->i_discard);
 	}
@@ -1280,7 +1326,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;
 
@@ -1294,6 +1340,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);
@@ -1301,10 +1348,19 @@
 		}
 
 		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;
+				ctx->i_discard = discard_file_name(arg);
+				if (ctx->i_discard == NULL)
+					nomem();
 				newae(ctx->i_ae, ctx->i_discard);
 			}
 			continue;
@@ -1329,6 +1385,9 @@
 				newae(ctx->i_ae, arg);
 			} else if (ctx->i_flags & CW_F_SHADOW) {
 				newae(ctx->i_ae, "-o");
+				ctx->i_discard = discard_file_name(arg);
+				if (ctx->i_discard == NULL)
+					nomem();
 				newae(ctx->i_ae, ctx->i_discard);
 			} else {
 				newae(ctx->i_ae, arg);
@@ -1352,12 +1411,25 @@
 
 	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");
+		ctx->i_discard = discard_file_name(NULL);
+
+		if (ctx->i_discard == NULL)
+			nomem();
 		newae(ctx->i_ae, ctx->i_discard);
 	}
 }
@@ -1467,6 +1539,7 @@
 	} while (!WIFEXITED(status) && !WIFSIGNALED(status));
 
 	(void) unlink(ctx->i_discard);
+	free(ctx->i_discard);
 
 	if (stat(ctx->i_stderr, &s) < 0) {
 		warn("stat failed on child cleanup");
@@ -1497,20 +1570,6 @@
 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);
