gcc: add -Wpoison-system-directories support

When packages cross-compile and use wrong paths like -I/usr/include, we
don't notice unless the headers happen to conflict, or the libraries are
incompatible.  Lets add support for the -Wpoison-system-directories that
many other distros have started adopting.

Now when you try to cross-compile and use a path like -I/usr/include, the
compiler throws an error.

This is based on work by Joseph Myers at CodeSourcery.  It has since been
improved greatly:
* The -W flags are always available.
* The configure option controls whether we warn/error by default.
* Handle all include paths in all category chains.
* Reject paths when they're specified even if they don't exist.

It is not currently upstream, nor has it been submitted at all.  There are
no plans to do so currently either.

BUG=chromium:488360
TEST=`cbuildbot chromiumos-sdk` passes  # tests arm/amd64/mipsel/x86
TEST=`cbuildbot panther_moblab-full whirlwind-release` pass
TEST=`cbuildbot {x32,arm64}-generic-full` has no new failures
TEST=x86_64-cros-linux-gnu-gcc throws warnings when using -I/usr/include

Change-Id: I7ee4cf7c3166ccf424d62d637af528686e19cb48
Reviewed-on: https://chromium-review.googlesource.com/272256
Trybot-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Luis Lozano <llozano@chromium.org>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
diff --git a/gcc/common.opt b/gcc/common.opt
index d44f5fe..b95edad 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -621,6 +621,10 @@
 Common Var(pedantic) Warning
 Issue warnings needed for strict compliance to the standard
 
+Wpoison-system-directories
+Common Var(flag_poison_system_directories) Init(POISON_SYSTEM_DIRECTORIES_DEFAULT) Warning
+Warn for -I and -L options using system directories if cross compiling
+
 Wshadow
 Common Var(warn_shadow) Warning
 Warn when one local variable shadows another
diff --git a/gcc/config.in b/gcc/config.in
index 9f8dee0..90b1dbf 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -144,6 +144,12 @@
 #endif
 
 
+/* Define to warn for use of native system header directories.  */
+#ifndef USED_FOR_TARGET
+#undef ENABLE_POISON_SYSTEM_DIRECTORIES
+#endif
+
+
 /* Define if you want all operations on RTL (the basic data structure of the
    optimizer and back end) to be checked for dynamic type safety at runtime.
    This is quite expensive. */
diff --git a/gcc/configure b/gcc/configure
index d4bd27e..1a58dc1 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -932,6 +932,7 @@
 enable_maintainer_mode
 enable_link_mutex
 enable_version_specific_runtime_libs
+enable_poison_system_directories
 enable_canonical_prefixes
 enable_plugin
 enable_host_shared
@@ -1665,6 +1666,8 @@
   --enable-version-specific-runtime-libs
                           specify that runtime libraries should be installed
                           in a compiler-specific directory
+  --enable-poison-system-directories
+                          warn for use of native system header directories
   --enable-canonical-prefixes
                           enable or disable prefix canonicalization
   --enable-plugin         enable plugin support
@@ -27908,6 +27911,19 @@
 
 fi
 
+# Check whether --enable-poison-system-directories was given.
+if test "${enable_poison_system_directories+set}" = set; then :
+  enableval=$enable_poison_system_directories;
+else
+  enable_poison_system_directories=no
+fi
+
+if test "x${enable_poison_system_directories}" = "xyes"; then
+
+$as_echo "#define ENABLE_POISON_SYSTEM_DIRECTORIES 1" >>confdefs.h
+
+fi
+
 # Substitute configuration variables
 
 
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 272e37b..b25775b 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -5510,6 +5510,16 @@
             1, [Define to enable prefix canonicalization.])
 fi
 
+AC_ARG_ENABLE([poison-system-directories],
+             AS_HELP_STRING([--enable-poison-system-directories],
+                            [warn for use of native system header directories]),,
+             [enable_poison_system_directories=no])
+if test "x${enable_poison_system_directories}" = "xyes"; then
+  AC_DEFINE([ENABLE_POISON_SYSTEM_DIRECTORIES],
+           [1],
+           [Define to warn for use of native system header directories])
+fi
+
 # Substitute configuration variables
 AC_SUBST(subdirs)
 AC_SUBST(srcdir)
diff --git a/gcc/defaults.h b/gcc/defaults.h
index f94ae17..678674a 100644
--- a/gcc/defaults.h
+++ b/gcc/defaults.h
@@ -1277,6 +1277,13 @@
 #define SET_RATIO(speed) MOVE_RATIO (speed)
 #endif
 
+/* Whether to warn about poisoned system directories by default.  */
+#ifdef ENABLE_POISON_SYSTEM_DIRECTORIES
+#define POISON_SYSTEM_DIRECTORIES_DEFAULT 1
+#else
+#define POISON_SYSTEM_DIRECTORIES_DEFAULT 0
+#endif
+
 /* Supply a default definition for FUNCTION_ARG_PADDING:
    usually pad upward, but pad short args downward on
    big-endian machines.  */
diff --git a/gcc/gcc.c b/gcc/gcc.c
index e187cd1..cc71782 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -751,6 +751,19 @@
     %{fvtable-verify=preinit: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}}"
 #endif
 
+#if POISON_SYSTEM_DIRECTORIES_DEFAULT
+# define POISON_SYSTEM_DIRECTORIES_DEFAULT_SPEC "\
+%{!Wpoison-system-directories:%{!Wno-poison-system-directories:\
+  --warn-poison-system-directories}}"
+#else
+# define POISON_SYSTEM_DIRECTORIES_DEFAULT_SPEC ""
+#endif
+#define POISON_SYSTEM_DIRECTORIES_SPEC \
+ POISON_SYSTEM_DIRECTORIES_DEFAULT_SPEC " \
+ %{Wpoison-system-directories:--warn-poison-system-directories}\
+ %{Wno-poison-system-directories:--no-warn-poison-system-directories}\
+ %{Werror=poison-system-directories:--error-poison-system-directories}"
+
 /* -u* was put back because both BSD and SysV seem to support it.  */
 /* %{static:} simply prevents an error message if the target machine
    doesn't handle -static.  */
@@ -773,6 +786,7 @@
    "%{fuse-ld=*:-fuse-ld=%*}\
     %X %{o*} %{e*} %{N} %{n} %{r}\
     %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}} " VTABLE_VERIFICATION_SPEC " \
+    " POISON_SYSTEM_DIRECTORIES_SPEC " \
     %{static:} %{L*} %(mfwrap) %(link_libgcc) " SANITIZER_EARLY_SPEC " %o\
     %{fopenmp|ftree-parallelize-loops=*:%:include(libgomp.spec)%(link_gomp)}\
     %{fcilkplus:%:include(libcilkrts.spec)%(link_cilkrts)}\
diff --git a/gcc/incpath.c b/gcc/incpath.c
index ddd856c..8c0dcec 100644
--- a/gcc/incpath.c
+++ b/gcc/incpath.c
@@ -28,6 +28,7 @@
 #include "intl.h"
 #include "incpath.h"
 #include "cppdefault.h"
+#include "diagnostic.h"
 
 /* Microsoft Windows does not natively support inodes.
    VMS has non-numeric inodes.  */
@@ -348,6 +349,45 @@
       add_sysroot_to_chain (sysroot, AFTER);
     }
 
+  /* Need to run here before processing below as that'll automatically cull
+     paths that do not exist.  However, we want to throw errors whenever the
+     build includes things like -I/usr/include/asdf even if it happens to not
+     exist on the current system.  */
+  if (flag_poison_system_directories)
+    {
+      unsigned int c;
+      unsigned int chains[] = { QUOTE, BRACKET, SYSTEM, AFTER };
+
+      /* Enable -Werror=poison-system-directories when -Werror and -Wno-error
+	 have not been set.
+
+	 Ideally this would be done in toplev's process_options, but this
+	 function runs before that one, so we inline it here instead.  */
+      if (POISON_SYSTEM_DIRECTORIES_DEFAULT
+	  && !global_options_set.x_warnings_are_errors
+	  && global_dc->classify_diagnostic[OPT_Wpoison_system_directories] ==
+	     DK_UNSPECIFIED)
+	diagnostic_classify_diagnostic (global_dc,
+				        OPT_Wpoison_system_directories,
+				        DK_ERROR, UNKNOWN_LOCATION);
+
+      for (c = 0; c < ARRAY_SIZE (chains); ++c)
+	{
+	  unsigned int chain = chains[c];
+	  struct cpp_dir *p;
+
+	  for (p = heads[chain]; p; p = p->next)
+	    if (!strncmp (p->name, "/usr/include", 12)
+	        || !strncmp (p->name, "/usr/local/include", 18)
+	        || !strncmp (p->name, "/usr/X11R6/include", 18)
+	        || !strncmp (p->name, "/lib", 4)
+	        || !strncmp (p->name, "/usr/local/lib", 14))
+	      warning (OPT_Wpoison_system_directories,
+		       "include location \"%s\" is unsafe for "
+		       "cross-compilation", p->name);
+	}
+    }
+
   /* Join the SYSTEM and AFTER chains.  Remove duplicates in the
      resulting SYSTEM chain.  */
   if (heads[SYSTEM])