summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
authorVladimír Čunát <vcunat@gmail.com>2017-08-12 10:09:41 +0200
committerVladimír Čunát <vcunat@gmail.com>2017-08-12 10:09:41 +0200
commit6899c7fdb989ce02f877ce5c0490a20ac4a64bf0 (patch)
tree68a2e9b25d3b4f0d0a70343b38d10bdc0111f20d /pkgs/build-support
parent9bcee1051a94d6ba2be64a19665f017542122b83 (diff)
parente207d1f04ad262f53e387e9e3956a2a9c421a66a (diff)
downloadnixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar.gz
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar.bz2
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar.lz
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar.xz
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.tar.zst
nixlib-6899c7fdb989ce02f877ce5c0490a20ac4a64bf0.zip
Merge branch 'master' into gcc-6
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/cc-wrapper/add-flags.sh76
-rw-r--r--pkgs/build-support/cc-wrapper/add-hardening.sh50
-rw-r--r--pkgs/build-support/cc-wrapper/cc-wrapper.sh113
-rw-r--r--pkgs/build-support/cc-wrapper/default.nix210
-rw-r--r--pkgs/build-support/cc-wrapper/gnat-wrapper.sh102
-rw-r--r--pkgs/build-support/cc-wrapper/gnatlink-wrapper.sh51
-rwxr-xr-xpkgs/build-support/cc-wrapper/ld-solaris-wrapper.sh35
-rw-r--r--pkgs/build-support/cc-wrapper/ld-wrapper.sh234
-rw-r--r--pkgs/build-support/cc-wrapper/macos-sierra-reexport-hack.bash106
-rw-r--r--pkgs/build-support/cc-wrapper/setup-hook.sh144
-rw-r--r--pkgs/build-support/cc-wrapper/utils.sh8
-rw-r--r--pkgs/build-support/docker/default.nix31
-rw-r--r--pkgs/build-support/emacs/wrapper.nix3
-rw-r--r--pkgs/build-support/fetchbower/default.nix4
-rw-r--r--pkgs/build-support/fetchpatch/default.nix9
-rw-r--r--pkgs/build-support/fetchurl/boot.nix3
-rw-r--r--pkgs/build-support/fetchurl/builder.sh2
-rw-r--r--pkgs/build-support/gcc-wrapper-old/builder.sh2
-rw-r--r--pkgs/build-support/setup-hooks/die.sh21
-rw-r--r--pkgs/build-support/setup-hooks/make-wrapper.sh48
-rw-r--r--pkgs/build-support/setup-hooks/setup-debug-info-dirs.sh5
-rw-r--r--pkgs/build-support/setup-hooks/wrap-gapps-hook.sh2
-rw-r--r--pkgs/build-support/trivial-builders.nix19
-rw-r--r--pkgs/build-support/vm/default.nix15
24 files changed, 810 insertions, 483 deletions
diff --git a/pkgs/build-support/cc-wrapper/add-flags.sh b/pkgs/build-support/cc-wrapper/add-flags.sh
index 5634c82aa285..4d28ba08d103 100644
--- a/pkgs/build-support/cc-wrapper/add-flags.sh
+++ b/pkgs/build-support/cc-wrapper/add-flags.sh
@@ -1,28 +1,88 @@
+# N.B. It may be a surprise that the derivation-specific variables are exported,
+# since this is just sourced by the wrapped binaries---the end consumers. This
+# is because one wrapper binary may invoke another (e.g. cc invoking ld). In
+# that case, it is cheaper/better to not repeat this step and let the forked
+# wrapped binary just inherit the work of the forker's wrapper script.
+
+var_templates=(
+    NIX_CC_WRAPPER+START_HOOK
+    NIX_CC_WRAPPER+EXEC_HOOK
+    NIX_LD_WRAPPER+START_HOOK
+    NIX_LD_WRAPPER+EXEC_HOOK
+
+    NIX+CFLAGS_COMPILE
+    NIX+CFLAGS_LINK
+    NIX+CXXSTDLIB_COMPILE
+    NIX+CXXSTDLIB_LINK
+    NIX+GNATFLAGS_COMPILE
+    NIX+IGNORE_LD_THROUGH_GCC
+    NIX+LDFLAGS
+    NIX+LDFLAGS_BEFORE
+    NIX+LDFLAGS_AFTER
+    NIX+LDFLAGS_HARDEN
+
+    NIX+SET_BUILD_ID
+    NIX+DONT_SET_RPATH
+    NIX+ENFORCE_NO_NATIVE
+)
+
+# Accumulate infixes for taking in the right input parameters. See setup-hook
+# for details.
+declare -a role_infixes=()
+if [ "${NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD:-}" ]; then
+    role_infixes+=(_BUILD_)
+fi
+if [ "${NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST:-}" ]; then
+    role_infixes+=(_)
+fi
+if [ "${NIX_CC_WRAPPER_@infixSalt@_TARGET_TARGET:-}" ]; then
+    role_infixes+=(_TARGET_)
+fi
+
+# We need to mangle names for hygiene, but also take parameters/overrides
+# from the environment.
+for var in "${var_templates[@]}"; do
+    outputVar="${var/+/_@infixSalt@_}"
+    export ${outputVar}+=''
+    # For each role we serve, we accumulate the input parameters into our own
+    # cc-wrapper-derivation-specific environment variables.
+    for infix in "${role_infixes[@]}"; do
+        inputVar="${var/+/${infix}}"
+        if [ -v "$inputVar" ]; then
+            export ${outputVar}+="${!outputVar:+ }${!inputVar}"
+        fi
+    done
+done
+
 # `-B@out@/bin' forces cc to use ld-wrapper.sh when calling ld.
-export NIX_CFLAGS_COMPILE="-B@out@/bin/ $NIX_CFLAGS_COMPILE"
+NIX_@infixSalt@_CFLAGS_COMPILE="-B@out@/bin/ $NIX_@infixSalt@_CFLAGS_COMPILE"
+
+# Export and assign separately in order that a failing $(..) will fail
+# the script.
 
 if [ -e @out@/nix-support/libc-cflags ]; then
-    export NIX_CFLAGS_COMPILE="$(cat @out@/nix-support/libc-cflags) $NIX_CFLAGS_COMPILE"
+    NIX_@infixSalt@_CFLAGS_COMPILE="$(< @out@/nix-support/libc-cflags) $NIX_@infixSalt@_CFLAGS_COMPILE"
 fi
 
 if [ -e @out@/nix-support/cc-cflags ]; then
-    export NIX_CFLAGS_COMPILE="$(cat @out@/nix-support/cc-cflags) $NIX_CFLAGS_COMPILE"
+    NIX_@infixSalt@_CFLAGS_COMPILE="$(< @out@/nix-support/cc-cflags) $NIX_@infixSalt@_CFLAGS_COMPILE"
 fi
 
 if [ -e @out@/nix-support/gnat-cflags ]; then
-    export NIX_GNATFLAGS_COMPILE="$(cat @out@/nix-support/gnat-cflags) $NIX_GNATFLAGS_COMPILE"
+    NIX_@infixSalt@_GNATFLAGS_COMPILE="$(< @out@/nix-support/gnat-cflags) $NIX_@infixSalt@_GNATFLAGS_COMPILE"
 fi
 
 if [ -e @out@/nix-support/libc-ldflags ]; then
-    export NIX_LDFLAGS+=" $(cat @out@/nix-support/libc-ldflags)"
+    NIX_@infixSalt@_LDFLAGS+=" $(< @out@/nix-support/libc-ldflags)"
 fi
 
 if [ -e @out@/nix-support/cc-ldflags ]; then
-    export NIX_LDFLAGS+=" $(cat @out@/nix-support/cc-ldflags)"
+    NIX_@infixSalt@_LDFLAGS+=" $(< @out@/nix-support/cc-ldflags)"
 fi
 
 if [ -e @out@/nix-support/libc-ldflags-before ]; then
-    export NIX_LDFLAGS_BEFORE="$(cat @out@/nix-support/libc-ldflags-before) $NIX_LDFLAGS_BEFORE"
+    NIX_@infixSalt@_LDFLAGS_BEFORE="$(< @out@/nix-support/libc-ldflags-before) $NIX_@infixSalt@_LDFLAGS_BEFORE"
 fi
 
-export NIX_CC_WRAPPER_FLAGS_SET=1
+# That way forked processes will not extend these environment variables again.
+export NIX_CC_WRAPPER_@infixSalt@_FLAGS_SET=1
diff --git a/pkgs/build-support/cc-wrapper/add-hardening.sh b/pkgs/build-support/cc-wrapper/add-hardening.sh
index b98833b3513b..aa8eb720486c 100644
--- a/pkgs/build-support/cc-wrapper/add-hardening.sh
+++ b/pkgs/build-support/cc-wrapper/add-hardening.sh
@@ -1,53 +1,69 @@
 hardeningFlags=(fortify stackprotector pic strictoverflow format relro bindnow)
-hardeningFlags+=("${hardeningEnable[@]}")
+# Intentionally word-split in case 'hardeningEnable' is defined in
+# Nix. Also, our bootstrap tools version of bash is old enough that
+# undefined arrays trip `set -u`.
+if [[ -v hardeningEnable[@] ]]; then
+  hardeningFlags+=(${hardeningEnable[@]})
+fi
 hardeningCFlags=()
 hardeningLDFlags=()
-hardeningDisable=${hardeningDisable:-""}
 
-hardeningDisable+=" @hardening_unsupported_flags@"
+declare -A hardeningDisableMap
+
+# Intentionally word-split in case 'hardeningDisable' is defined in Nix.
+for flag in ${hardeningDisable[@]:-IGNORED_KEY} @hardening_unsupported_flags@
+do
+  hardeningDisableMap[$flag]=1
+done
 
-if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: Value of '$hardeningDisable': $hardeningDisable >&2; fi
+if [[ -n "${NIX_DEBUG:-}" ]]; then
+  printf 'HARDENING: disabled flags:' >&2
+  (( "${#hardeningDisableMap[@]}" )) && printf ' %q' "${!hardeningDisableMap[@]}" >&2
+  echo >&2
+fi
 
-if [[ ! $hardeningDisable =~ "all" ]]; then
-  if [[ -n "$NIX_DEBUG" ]]; then echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2; fi
+if [[ -z "${hardeningDisableMap[all]:-}" ]]; then
+  if [[ -n "${NIX_DEBUG:-}" ]]; then
+    echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2;
+  fi
   for flag in "${hardeningFlags[@]}"
   do
-    if [[ ! "${hardeningDisable}" =~ "$flag" ]]; then
+    if [[ -z "${hardeningDisableMap[$flag]:-}" ]]; then
       case $flag in
         fortify)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling fortify >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling fortify >&2; fi
           hardeningCFlags+=('-O2' '-D_FORTIFY_SOURCE=2')
           ;;
         stackprotector)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling stackprotector >&2; fi
-          hardeningCFlags+=('-fstack-protector-strong' '--param ssp-buffer-size=4')
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling stackprotector >&2; fi
+          hardeningCFlags+=('-fstack-protector-strong' '--param' 'ssp-buffer-size=4')
           ;;
         pie)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling CFlags -fPIE >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling CFlags -fPIE >&2; fi
           hardeningCFlags+=('-fPIE')
           if [[ ! ("$*" =~ " -shared " || "$*" =~ " -static ") ]]; then
-            if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling LDFlags -pie >&2; fi
+            if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling LDFlags -pie >&2; fi
             hardeningLDFlags+=('-pie')
           fi
           ;;
         pic)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling pic >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling pic >&2; fi
           hardeningCFlags+=('-fPIC')
           ;;
         strictoverflow)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling strictoverflow >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling strictoverflow >&2; fi
           hardeningCFlags+=('-fno-strict-overflow')
           ;;
         format)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling format >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling format >&2; fi
           hardeningCFlags+=('-Wformat' '-Wformat-security' '-Werror=format-security')
           ;;
         relro)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling relro >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling relro >&2; fi
           hardeningLDFlags+=('-z' 'relro')
           ;;
         bindnow)
-          if [[ -n "$NIX_DEBUG" ]]; then echo HARDENING: enabling bindnow >&2; fi
+          if [[ -n "${NIX_DEBUG:-}" ]]; then echo HARDENING: enabling bindnow >&2; fi
           hardeningLDFlags+=('-z' 'now')
           ;;
         *)
diff --git a/pkgs/build-support/cc-wrapper/cc-wrapper.sh b/pkgs/build-support/cc-wrapper/cc-wrapper.sh
index 3ccdc34db5b2..e5a3a5818519 100644
--- a/pkgs/build-support/cc-wrapper/cc-wrapper.sh
+++ b/pkgs/build-support/cc-wrapper/cc-wrapper.sh
@@ -1,17 +1,24 @@
-#! @shell@ -e
+#! @shell@
+set -eu -o pipefail
+shopt -s nullglob
+
 path_backup="$PATH"
-if [ -n "@coreutils_bin@" ]; then
-  PATH="@coreutils_bin@/bin:@gnugrep_bin@/bin"
-fi
 
-if [ -n "$NIX_CC_WRAPPER_START_HOOK" ]; then
-    source "$NIX_CC_WRAPPER_START_HOOK"
+# That @-vars are substituted separately from bash evaluation makes
+# shellcheck think this, and others like it, are useless conditionals.
+# shellcheck disable=SC2157
+if [[ -n "@coreutils_bin@" && -n "@gnugrep_bin@" ]]; then
+    PATH="@coreutils_bin@/bin:@gnugrep_bin@/bin"
 fi
 
-if [ -z "$NIX_CC_WRAPPER_FLAGS_SET" ]; then
+if [ -z "${NIX_CC_WRAPPER_@infixSalt@_FLAGS_SET:-}" ]; then
     source @out@/nix-support/add-flags.sh
 fi
 
+if [ -n "$NIX_CC_WRAPPER_@infixSalt@_START_HOOK" ]; then
+    source "$NIX_CC_WRAPPER_@infixSalt@_START_HOOK"
+fi
+
 source @out@/nix-support/utils.sh
 
 
@@ -19,16 +26,17 @@ source @out@/nix-support/utils.sh
 # For instance, figure out if linker flags should be passed.
 # GCC prints annoying warnings when they are not needed.
 dontLink=0
-getVersion=0
 nonFlagArgs=0
+# shellcheck disable=SC2193
 [[ "@prog@" = *++ ]] && isCpp=1 || isCpp=0
 cppInclude=1
 
 expandResponseParams "$@"
-n=0
-while [ $n -lt ${#params[*]} ]; do
+declare -i n=0
+nParams=${#params[@]}
+while [ "$n" -lt "$nParams" ]; do
     p=${params[n]}
-    p2=${params[$((n+1))]}
+    p2=${params[n+1]:-} # handle `p` being last one
     if [ "$p" = -c ]; then
         dontLink=1
     elif [ "$p" = -S ]; then
@@ -55,10 +63,10 @@ while [ $n -lt ${#params[*]} ]; do
         nonFlagArgs=1
     elif [ "$p" = -m32 ]; then
         if [ -e @out@/nix-support/dynamic-linker-m32 ]; then
-            NIX_LDFLAGS="$NIX_LDFLAGS -dynamic-linker $(cat @out@/nix-support/dynamic-linker-m32)"
+            NIX_@infixSalt@_LDFLAGS+=" -dynamic-linker $(< @out@/nix-support/dynamic-linker-m32)"
         fi
     fi
-    n=$((n + 1))
+    n+=1
 done
 
 # If we pass a flag like -Wl, then gcc will call the linker unless it
@@ -71,39 +79,40 @@ if [ "$nonFlagArgs" = 0 ]; then
 fi
 
 # Optionally filter out paths not refering to the store.
-if [ "$NIX_ENFORCE_PURITY" = 1 -a -n "$NIX_STORE" ]; then
+if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "$NIX_STORE" ]]; then
     rest=()
-    n=0
-    while [ $n -lt ${#params[*]} ]; do
+    nParams=${#params[@]}
+    declare -i n=0
+    while [ "$n" -lt "$nParams" ]; do
         p=${params[n]}
-        p2=${params[$((n+1))]}
+        p2=${params[n+1]:-} # handle `p` being last one
         if [ "${p:0:3}" = -L/ ] && badPath "${p:2}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "$p" = -L ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         elif [ "${p:0:3}" = -I/ ] && badPath "${p:2}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "$p" = -I ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         elif [ "$p" = -isystem ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         else
             rest+=("$p")
         fi
-        n=$((n + 1))
+        n+=1
     done
     params=("${rest[@]}")
 fi
 
 
 # Clear march/mtune=native -- they bring impurity.
-if [ "$NIX_ENFORCE_NO_NATIVE" = 1 ]; then
+if [ "$NIX_@infixSalt@_ENFORCE_NO_NATIVE" = 1 ]; then
     rest=()
-    for i in "${params[@]}"; do
-        if [[ "$i" = -m*=native ]]; then
-            skip $i
+    for p in "${params[@]}"; do
+        if [[ "$p" = -m*=native ]]; then
+            skip "$p"
         else
-            rest+=("$i")
+            rest+=("$p")
         fi
     done
     params=("${rest[@]}")
@@ -111,37 +120,36 @@ fi
 
 if [[ "$isCpp" = 1 ]]; then
     if [[ "$cppInclude" = 1 ]]; then
-        NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE ${NIX_CXXSTDLIB_COMPILE-@default_cxx_stdlib_compile@}"
+        NIX_@infixSalt@_CFLAGS_COMPILE+=" ${NIX_@infixSalt@_CXXSTDLIB_COMPILE-@default_cxx_stdlib_compile@}"
     fi
-    NIX_CFLAGS_LINK="$NIX_CFLAGS_LINK $NIX_CXXSTDLIB_LINK"
+    NIX_@infixSalt@_CFLAGS_LINK+=" $NIX_@infixSalt@_CXXSTDLIB_LINK"
 fi
 
-LD=@ldPath@/ld
 source @out@/nix-support/add-hardening.sh
 
 # Add the flags for the C compiler proper.
-extraAfter=($NIX_CFLAGS_COMPILE ${hardeningCFlags[@]})
+extraAfter=($NIX_@infixSalt@_CFLAGS_COMPILE "${hardeningCFlags[@]}")
 extraBefore=()
 
 if [ "$dontLink" != 1 ]; then
 
     # Add the flags that should only be passed to the compiler when
     # linking.
-    extraAfter+=($NIX_CFLAGS_LINK ${hardeningLDFlags[@]})
+    extraAfter+=($NIX_@infixSalt@_CFLAGS_LINK "${hardeningLDFlags[@]}")
 
     # Add the flags that should be passed to the linker (and prevent
-    # `ld-wrapper' from adding NIX_LDFLAGS again).
-    for i in $NIX_LDFLAGS_BEFORE; do
-        extraBefore=(${extraBefore[@]} "-Wl,$i")
+    # `ld-wrapper' from adding NIX_@infixSalt@_LDFLAGS again).
+    for i in $NIX_@infixSalt@_LDFLAGS_BEFORE; do
+        extraBefore+=("-Wl,$i")
     done
-    for i in $NIX_LDFLAGS; do
+    for i in $NIX_@infixSalt@_LDFLAGS; do
         if [ "${i:0:3}" = -L/ ]; then
             extraAfter+=("$i")
         else
             extraAfter+=("-Wl,$i")
         fi
     done
-    export NIX_LDFLAGS_SET=1
+    export NIX_@infixSalt@_LDFLAGS_SET=1
 fi
 
 # As a very special hack, if the arguments are just `-v', then don't
@@ -154,24 +162,21 @@ if [ "$*" = -v ]; then
 fi
 
 # Optionally print debug info.
-if [ -n "$NIX_DEBUG" ]; then
-  echo "original flags to @prog@:" >&2
-  for i in "${params[@]}"; do
-      echo "  $i" >&2
-  done
-  echo "extraBefore flags to @prog@:" >&2
-  for i in ${extraBefore[@]}; do
-      echo "  $i" >&2
-  done
-  echo "extraAfter flags to @prog@:" >&2
-  for i in ${extraAfter[@]}; do
-      echo "  $i" >&2
-  done
+if [ -n "${NIX_DEBUG:-}" ]; then
+    set +u # Old bash workaround, see ld-wrapper for explanation.
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" "${extraBefore[@]}"  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" "${params[@]}" >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" "${extraAfter[@]}" >&2
+    set -u
 fi
 
-if [ -n "$NIX_CC_WRAPPER_EXEC_HOOK" ]; then
-    source "$NIX_CC_WRAPPER_EXEC_HOOK"
+if [ -n "$NIX_CC_WRAPPER_@infixSalt@_EXEC_HOOK" ]; then
+    source "$NIX_CC_WRAPPER_@infixSalt@_EXEC_HOOK"
 fi
 
 PATH="$path_backup"
-exec @prog@ ${extraBefore[@]} "${params[@]}" "${extraAfter[@]}"
+set +u # Old bash workaround, see above.
+exec @prog@ "${extraBefore[@]}" "${params[@]}" "${extraAfter[@]}"
diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix
index 7df9615653f9..502362514ebc 100644
--- a/pkgs/build-support/cc-wrapper/default.nix
+++ b/pkgs/build-support/cc-wrapper/default.nix
@@ -12,6 +12,7 @@
 , isGNU ? false, isClang ? cc.isClang or false, gnugrep ? null
 , buildPackages ? {}, hostPlatform, targetPlatform
 , runCommand ? null
+, useMacosReexportHack ? false
 }:
 
 with stdenv.lib;
@@ -52,75 +53,28 @@ let
     "-isystem $(echo -n ${cc.gcc}/include/c++/*) -isystem $(echo -n ${cc.gcc}/include/c++/*)/$(${cc.gcc}/bin/gcc -dumpmachine)";
 
   dashlessTarget = stdenv.lib.replaceStrings ["-"] ["_"] targetPlatform.config;
-  # TODO(@Ericson2314) Make unconditional
-  infixSalt  = stdenv.lib.optionalString (targetPlatform != hostPlatform) dashlessTarget;
-  infixSalt_ = stdenv.lib.optionalString (targetPlatform != hostPlatform) (dashlessTarget + "_");
-  _infixSalt = stdenv.lib.optionalString (targetPlatform != hostPlatform) ("_" + dashlessTarget);
-
-  # We want to prefix all NIX_ flags with the target triple
-  preWrap = textFile:
-    # TODO: Do even when not cross on next mass-rebuild
-    # TODO: use @target_tripple@ for consistency
-    if targetPlatform == hostPlatform
-    then textFile
-    else runCommand "sed-nix-env-vars" {} (''
-      cp --no-preserve=mode ${textFile} $out
-
-      sed -i $out \
-        -e 's^NIX_^NIX_${infixSalt_}^g' \
-        -e 's^addCVars^addCVars${_infixSalt}^g' \
-        -e 's^\[ -z "\$crossConfig" \]^\[\[ "${builtins.toString (targetPlatform != hostPlatform)}" || -z "$crossConfig" \]\]^g'
-
-    '' + stdenv.lib.optionalString (textFile == ./setup-hook.sh) ''
-      cat << 'EOF' >> $out
-        for CMD in ar as nm objcopy ranlib strip strings size ld windres
-        do
-          # which is not part of stdenv, but compgen will do for now
-          if
-            PATH=$_PATH type -p ${prefix}$CMD > /dev/null
-          then
-            export ''$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=${prefix}''${CMD};
-          fi
-        done
-      EOF
-
-      sed -i $out -e 's_envHooks_crossEnvHooks_g'
-    '' + ''
-
-      # NIX_ things which we don't both use and define, we revert them
-      #asymmetric=$(
-      #  for pre in "" "\\$"
-      #  do
-      #    grep -E -ho $pre'NIX_[a-zA-Z_]*' ./* | sed 's/\$//' | sort | uniq
-      #  done | sort | uniq -c | sort -nr | sed -n 's/^1 NIX_//gp')
-
-      # hard-code for now
-      asymmetric=("CXXSTDLIB_COMPILE" "CC")
-
-      # The ([^a-zA-Z_]|$) bussiness is to ensure environment variables that
-      # begin with `NIX_CC` don't also get blacklisted.
-      for var in "''${asymmetric[@]}"
-      do
-        sed -i $out -E -e "s~NIX_${infixSalt_}$var([^a-zA-Z_]|$)~NIX_$var\1~g"
-      done
-    '');
-
-  # The dynamic linker has different names on different platforms.
+
+  # The "infix salt" is a arbitrary string added in the middle of env vars
+  # defined by cc-wrapper's hooks so that multiple cc-wrappers can be used
+  # without interfering. For the moment, it is defined as the target triple,
+  # adjusted to be a valid bash identifier. This should be considered an
+  # unstable implementation detail, however.
+  infixSalt = dashlessTarget;
+
+  # The dynamic linker has different names on different platforms. This is a
+  # shell glob that ought to match it.
   dynamicLinker =
-    if !nativeLibc then
-      (if targetPlatform.system == "i686-linux"     then "ld-linux.so.2" else
-       if targetPlatform.system == "x86_64-linux"   then "ld-linux-x86-64.so.2" else
-       # ARM with a wildcard, which can be "" or "-armhf".
-       if targetPlatform.isArm32                    then "ld-linux*.so.3" else
-       if targetPlatform.system == "aarch64-linux"  then "ld-linux-aarch64.so.1" else
-       if targetPlatform.system == "powerpc-linux"  then "ld.so.1" else
-       if targetPlatform.system == "mips64el-linux" then "ld.so.1" else
-       if targetPlatform.system == "x86_64-darwin"  then "/usr/lib/dyld" else
-       if stdenv.lib.hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1" else
-       builtins.trace
-         "Don't know the name of the dynamic linker for platform ${targetPlatform.config}, so guessing instead."
-         null)
-    else "";
+    /**/ if libc == null then null
+    else if targetPlatform.system == "i686-linux"     then "${libc_lib}/lib/ld-linux.so.2"
+    else if targetPlatform.system == "x86_64-linux"   then "${libc_lib}/lib/ld-linux-x86-64.so.2"
+    # ARM with a wildcard, which can be "" or "-armhf".
+    else if targetPlatform.isArm32                    then "${libc_lib}/lib/ld-linux*.so.3"
+    else if targetPlatform.system == "aarch64-linux"  then "${libc_lib}/lib/ld-linux-aarch64.so.1"
+    else if targetPlatform.system == "powerpc-linux"  then "${libc_lib}/lib/ld.so.1"
+    else if targetPlatform.system == "mips64el-linux" then "${libc_lib}/lib/ld.so.1"
+    else if targetPlatform.system == "x86_64-darwin"  then "/usr/lib/dyld"
+    else if stdenv.lib.hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
+    else null;
 
   expand-response-params = if buildPackages.stdenv.cc or null != null && buildPackages.stdenv.cc != "/dev/null"
   then buildPackages.stdenv.mkDerivation {
@@ -147,21 +101,24 @@ stdenv.mkDerivation {
   inherit cc shell libc_bin libc_dev libc_lib binutils_bin coreutils_bin;
   gnugrep_bin = if nativeTools then "" else gnugrep;
 
+  binPrefix = prefix;
+  inherit infixSalt;
+
   outputs = [ "out" "man" ];
 
   passthru = {
     inherit libc nativeTools nativeLibc nativePrefix isGNU isClang default_cxx_stdlib_compile
-            prefix infixSalt infixSalt_ _infixSalt;
+            prefix;
 
     emacsBufferSetup = pkgs: ''
       ; We should handle propagation here too
       (mapc (lambda (arg)
         (when (file-directory-p (concat arg "/include"))
-          (setenv "NIX_${infixSalt_}CFLAGS_COMPILE" (concat (getenv "NIX_${infixSalt_}CFLAGS_COMPILE") " -isystem " arg "/include")))
+          (setenv "NIX_${infixSalt}_CFLAGS_COMPILE" (concat (getenv "NIX_${infixSalt}_CFLAGS_COMPILE") " -isystem " arg "/include")))
         (when (file-directory-p (concat arg "/lib"))
-          (setenv "NIX_${infixSalt_}LDFLAGS" (concat (getenv "NIX_${infixSalt_}LDFLAGS") " -L" arg "/lib")))
+          (setenv "NIX_${infixSalt}_LDFLAGS" (concat (getenv "NIX_${infixSalt}_LDFLAGS") " -L" arg "/lib")))
         (when (file-directory-p (concat arg "/lib64"))
-          (setenv "NIX_${infixSalt_}LDFLAGS" (concat (getenv "NIX_${infixSalt_}LDFLAGS") " -L" arg "/lib64")))) '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
+          (setenv "NIX_${infixSalt}_LDFLAGS" (concat (getenv "NIX_${infixSalt}_LDFLAGS") " -L" arg "/lib64")))) '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
     '';
   };
 
@@ -178,39 +135,39 @@ stdenv.mkDerivation {
       }
     ''
 
-      # TODO(@Ericson2314): Unify logic next hash break
-    + optionalString (libc != null) (if (targetPlatform.isDarwin) then ''
-      echo $dynamicLinker > $out/nix-support/dynamic-linker
+    + optionalString (libc != null) (''
+      if [[ -z ''${dynamicLinker+x} ]]; then
+        echo "Don't know the name of the dynamic linker for platform '${targetPlatform.config}', so guessing instead." >&2
+        dynamicLinker="${libc_lib}/lib/ld*.so.?"
+      fi
 
-      echo "export LD_DYLD_PATH=\"$dynamicLinker\"" >> $out/nix-support/setup-hook
-    '' else if dynamicLinker != null then ''
-      dynamicLinker="${libc_lib}/lib/$dynamicLinker"
-      echo $dynamicLinker > $out/nix-support/dynamic-linker
+      # Expand globs to fill array of options
+      dynamicLinker=($dynamicLinker)
 
-      if [ -e ${libc_lib}/lib/32/ld-linux.so.2 ]; then
-        echo ${libc_lib}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
-      fi
+      case ''${#dynamicLinker[@]} in
+        0) echo "No dynamic linker found for platform '${targetPlatform.config}'." >&2;;
+        1) echo "Using dynamic linker: '$dynamicLinker'" >&2;;
+        *) echo "Multiple dynamic linkers found for platform '${targetPlatform.config}'." >&2;;
+      esac
 
-      # The dynamic linker is passed in `ldflagsBefore' to allow
-      # explicit overrides of the dynamic linker by callers to gcc/ld
-      # (the *last* value counts, so ours should come first).
-      echo "-dynamic-linker" $dynamicLinker > $out/nix-support/libc-ldflags-before
-    '' else ''
-      dynamicLinker=`eval 'echo $libc/lib/ld*.so.?'`
       if [ -n "$dynamicLinker" ]; then
         echo $dynamicLinker > $out/nix-support/dynamic-linker
 
+    '' + (if targetPlatform.isDarwin then ''
+        printf "export LD_DYLD_PATH=%q\n" "$dynamicLinker" >> $out/nix-support/setup-hook
+    '' else ''
         if [ -e ${libc_lib}/lib/32/ld-linux.so.2 ]; then
           echo ${libc_lib}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
         fi
 
-        ldflagsBefore="-dynamic-linker $dlinker"
+        ldflagsBefore=(-dynamic-linker "$dynamicLinker")
+    '') + ''
       fi
 
       # The dynamic linker is passed in `ldflagsBefore' to allow
       # explicit overrides of the dynamic linker by callers to gcc/ld
       # (the *last* value counts, so ours should come first).
-      echo "$ldflagsBefore" > $out/nix-support/libc-ldflags-before
+      printWords "''${ldflagsBefore[@]}" > $out/nix-support/libc-ldflags-before
     '')
 
     + optionalString (libc != null) ''
@@ -278,17 +235,17 @@ stdenv.mkDerivation {
       # Propagate the wrapped cc so that if you install the wrapper,
       # you get tools like gcov, the manpages, etc. as well (including
       # for binutils and Glibc).
-      echo ${cc} ${binutils_bin} ${if libc == null then "" else libc_bin} > $out/nix-support/propagated-user-env-packages
-      echo ${cc.man or ""}  > $man/nix-support/propagated-user-env-packages
+      printWords ${cc} ${binutils_bin} ${if libc == null then "" else libc_bin} > $out/nix-support/propagated-user-env-packages
+      printWords ${cc.man or ""}  > $man/nix-support/propagated-user-env-packages
 
-      echo ${toString extraPackages} > $out/nix-support/propagated-native-build-inputs
+      printWords ${toString extraPackages} > $out/nix-support/propagated-native-build-inputs
     ''
 
     + optionalString (targetPlatform.isSunOS && nativePrefix != "") ''
       # Solaris needs an additional ld wrapper.
       ldPath="${nativePrefix}/bin"
       exec="$ldPath/${prefix}ld"
-      wrap ld-solaris ${preWrap ./ld-solaris-wrapper.sh}
+      wrap ld-solaris ${./ld-solaris-wrapper.sh}
     '')
 
     + ''
@@ -299,64 +256,75 @@ stdenv.mkDerivation {
         ln -s $ldPath/${prefix}as $out/bin/${prefix}as
       fi
 
-      wrap ${prefix}ld ${preWrap ./ld-wrapper.sh} ''${ld:-$ldPath/${prefix}ld}
+    '' + (if !useMacosReexportHack then ''
+      wrap ${prefix}ld ${./ld-wrapper.sh} ''${ld:-$ldPath/${prefix}ld}
+    '' else ''
+      ldInner="${prefix}ld-reexport-delegate"
+      wrap "$ldInner" ${./macos-sierra-reexport-hack.bash} ''${ld:-$ldPath/${prefix}ld}
+      wrap "${prefix}ld" ${./ld-wrapper.sh} "$out/bin/$ldInner"
+      unset ldInner
+    '') + ''
 
       if [ -e ${binutils_bin}/bin/${prefix}ld.gold ]; then
-        wrap ${prefix}ld.gold ${preWrap ./ld-wrapper.sh} ${binutils_bin}/bin/${prefix}ld.gold
+        wrap ${prefix}ld.gold ${./ld-wrapper.sh} ${binutils_bin}/bin/${prefix}ld.gold
       fi
 
       if [ -e ${binutils_bin}/bin/ld.bfd ]; then
-        wrap ${prefix}ld.bfd ${preWrap ./ld-wrapper.sh} ${binutils_bin}/bin/${prefix}ld.bfd
+        wrap ${prefix}ld.bfd ${./ld-wrapper.sh} ${binutils_bin}/bin/${prefix}ld.bfd
       fi
 
-      export real_cc=${prefix}cc
-      export real_cxx=${prefix}c++
+      # We export environment variables pointing to the wrapped nonstandard
+      # cmds, lest some lousy configure script use those to guess compiler
+      # version.
+      export named_cc=${prefix}cc
+      export named_cxx=${prefix}c++
+
       export default_cxx_stdlib_compile="${default_cxx_stdlib_compile}"
 
       if [ -e $ccPath/${prefix}gcc ]; then
-        wrap ${prefix}gcc ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}gcc
+        wrap ${prefix}gcc ${./cc-wrapper.sh} $ccPath/${prefix}gcc
         ln -s ${prefix}gcc $out/bin/${prefix}cc
-        export real_cc=${prefix}gcc
-        export real_cxx=${prefix}g++
+        export named_cc=${prefix}gcc
+        export named_cxx=${prefix}g++
       elif [ -e $ccPath/clang ]; then
-        wrap ${prefix}clang ${preWrap ./cc-wrapper.sh} $ccPath/clang
+        wrap ${prefix}clang ${./cc-wrapper.sh} $ccPath/clang
         ln -s ${prefix}clang $out/bin/${prefix}cc
-        export real_cc=clang
-        export real_cxx=clang++
+        export named_cc=${prefix}clang
+        export named_cxx=${prefix}clang++
       fi
 
       if [ -e $ccPath/${prefix}g++ ]; then
-        wrap ${prefix}g++ ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}g++
+        wrap ${prefix}g++ ${./cc-wrapper.sh} $ccPath/${prefix}g++
         ln -s ${prefix}g++ $out/bin/${prefix}c++
       elif [ -e $ccPath/clang++ ]; then
-        wrap ${prefix}clang++ ${preWrap ./cc-wrapper.sh} $ccPath/clang++
+        wrap ${prefix}clang++ ${./cc-wrapper.sh} $ccPath/clang++
         ln -s ${prefix}clang++ $out/bin/${prefix}c++
       fi
 
       if [ -e $ccPath/cpp ]; then
-        wrap ${prefix}cpp ${preWrap ./cc-wrapper.sh} $ccPath/cpp
+        wrap ${prefix}cpp ${./cc-wrapper.sh} $ccPath/cpp
       fi
     ''
 
     + optionalString cc.langFortran or false ''
-      wrap ${prefix}gfortran ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}gfortran
+      wrap ${prefix}gfortran ${./cc-wrapper.sh} $ccPath/${prefix}gfortran
       ln -sv ${prefix}gfortran $out/bin/${prefix}g77
       ln -sv ${prefix}gfortran $out/bin/${prefix}f77
     ''
 
     + optionalString cc.langJava or false ''
-      wrap ${prefix}gcj ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}gcj
+      wrap ${prefix}gcj ${./cc-wrapper.sh} $ccPath/${prefix}gcj
     ''
 
     + optionalString cc.langGo or false ''
-      wrap ${prefix}gccgo ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}gccgo
+      wrap ${prefix}gccgo ${./cc-wrapper.sh} $ccPath/${prefix}gccgo
     ''
 
     + optionalString cc.langAda or false ''
-      wrap ${prefix}gnatgcc ${preWrap ./cc-wrapper.sh} $ccPath/${prefix}gnatgcc
-      wrap ${prefix}gnatmake ${preWrap ./gnat-wrapper.sh} $ccPath/${prefix}gnatmake
-      wrap ${prefix}gnatbind ${preWrap ./gnat-wrapper.sh} $ccPath/${prefix}gnatbind
-      wrap ${prefix}gnatlink ${preWrap ./gnatlink-wrapper.sh} $ccPath/${prefix}gnatlink
+      wrap ${prefix}gnatgcc ${./cc-wrapper.sh} $ccPath/${prefix}gnatgcc
+      wrap ${prefix}gnatmake ${./gnat-wrapper.sh} $ccPath/${prefix}gnatmake
+      wrap ${prefix}gnatbind ${./gnat-wrapper.sh} $ccPath/${prefix}gnatbind
+      wrap ${prefix}gnatlink ${./gnatlink-wrapper.sh} $ccPath/${prefix}gnatlink
     ''
 
     + optionalString cc.langVhdl or false ''
@@ -364,7 +332,7 @@ stdenv.mkDerivation {
     ''
 
     + ''
-      substituteAll ${preWrap ./setup-hook.sh} $out/nix-support/setup-hook.tmp
+      substituteAll ${./setup-hook.sh} $out/nix-support/setup-hook.tmp
       cat $out/nix-support/setup-hook.tmp >> $out/nix-support/setup-hook
       rm $out/nix-support/setup-hook.tmp
 
@@ -383,9 +351,9 @@ stdenv.mkDerivation {
     ''
 
     + ''
-      substituteAll ${preWrap ./add-flags.sh} $out/nix-support/add-flags.sh
-      substituteAll ${preWrap ./add-hardening.sh} $out/nix-support/add-hardening.sh
-      substituteAll ${preWrap ./utils.sh} $out/nix-support/utils.sh
+      substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
+      substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
+      substituteAll ${./utils.sh} $out/nix-support/utils.sh
     ''
     + extraBuildCommands;
 
@@ -403,5 +371,7 @@ stdenv.mkDerivation {
     { description =
         stdenv.lib.attrByPath ["meta" "description"] "System C compiler" cc_
         + " (wrapper script)";
-    };
+  } // optionalAttrs useMacosReexportHack {
+    platforms = stdenv.lib.platforms.darwin;
+  };
 }
diff --git a/pkgs/build-support/cc-wrapper/gnat-wrapper.sh b/pkgs/build-support/cc-wrapper/gnat-wrapper.sh
index 0d74527dd8ad..1a09f4841098 100644
--- a/pkgs/build-support/cc-wrapper/gnat-wrapper.sh
+++ b/pkgs/build-support/cc-wrapper/gnat-wrapper.sh
@@ -1,15 +1,24 @@
-#! @shell@ -e
+#! @shell@
+set -eu -o pipefail
+shopt -s nullglob
+
+# N.B. Gnat is not used during bootstrapping, so we don't need to
+# worry about the old bash empty array `set -u` workarounds.
+
 path_backup="$PATH"
+
+# phase separation makes this look useless
+# shellcheck disable=SC2157
 if [ -n "@coreutils_bin@" ]; then
-  PATH="@coreutils_bin@/bin"
+    PATH="@coreutils_bin@/bin"
 fi
 
-if [ -n "$NIX_GNAT_WRAPPER_START_HOOK" ]; then
-    source "$NIX_GNAT_WRAPPER_START_HOOK"
+if [ -z "${NIX_@infixSalt@_GNAT_WRAPPER_FLAGS_SET:-}" ]; then
+    source @out@/nix-support/add-flags.sh
 fi
 
-if [ -z "$NIX_GNAT_WRAPPER_FLAGS_SET" ]; then
-    source @out@/nix-support/add-flags.sh
+if [ -n "$NIX_@infixSalt@_GNAT_WRAPPER_START_HOOK" ]; then
+    source "$NIX_@infixSalt@_GNAT_WRAPPER_START_HOOK"
 fi
 
 source @out@/nix-support/utils.sh
@@ -18,7 +27,6 @@ source @out@/nix-support/utils.sh
 # Figure out if linker flags should be passed.  GCC prints annoying
 # warnings when they are not needed.
 dontLink=0
-getVersion=0
 nonFlagArgs=0
 
 for i in "$@"; do
@@ -30,7 +38,7 @@ for i in "$@"; do
         nonFlagArgs=1
     elif [ "$i" = -m32 ]; then
         if [ -e @out@/nix-support/dynamic-linker-m32 ]; then
-            NIX_LDFLAGS="$NIX_LDFLAGS -dynamic-linker $(cat @out@/nix-support/dynamic-linker-m32)"
+            NIX_@infixSalt@_LDFLAGS+=" -dynamic-linker $(< @out@/nix-support/dynamic-linker-m32)"
         fi
     fi
 done
@@ -47,37 +55,33 @@ fi
 
 # Optionally filter out paths not refering to the store.
 params=("$@")
-if [ "$NIX_ENFORCE_PURITY" = 1 -a -n "$NIX_STORE" ]; then
+if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "$NIX_STORE" ]]; then
     rest=()
-    n=0
-    while [ $n -lt ${#params[*]} ]; do
-        p=${params[n]}
-        p2=${params[$((n+1))]}
+    for p in "${params[@]}"; do
         if [ "${p:0:3}" = -L/ ] && badPath "${p:2}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "${p:0:3}" = -I/ ] && badPath "${p:2}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "${p:0:4}" = -aI/ ] && badPath "${p:3}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "${p:0:4}" = -aO/ ] && badPath "${p:3}"; then
-            skip $p
+            skip "${p:2}"
         else
             rest+=("$p")
         fi
-        n=$((n + 1))
     done
     params=("${rest[@]}")
 fi
 
 
 # Clear march/mtune=native -- they bring impurity.
-if [ "$NIX_ENFORCE_NO_NATIVE" = 1 ]; then
+if [ "$NIX_@infixSalt@_ENFORCE_NO_NATIVE" = 1 ]; then
     rest=()
-    for i in "${params[@]}"; do
-        if [[ "$i" = -m*=native ]]; then
-            skip $i
+    for p in "${params[@]}"; do
+        if [[ "$p" = -m*=native ]]; then
+            skip "$p"
         else
-            rest+=("$i")
+            rest+=("$p")
         fi
     done
     params=("${rest[@]}")
@@ -85,38 +89,42 @@ fi
 
 
 # Add the flags for the GNAT compiler proper.
-extraAfter=($NIX_GNATFLAGS_COMPILE)
+extraAfter=($NIX_@infixSalt@_GNATFLAGS_COMPILE)
 extraBefore=()
 
-if [ "`basename $0`x" = "gnatmakex" ]; then
-  extraBefore=("--GNATBIND=@out@/bin/gnatbind --GNATLINK=@out@/bin/gnatlink ")
+if [ "$(basename "$0")x" = "gnatmakex" ]; then
+  extraBefore=("--GNATBIND=@out@/bin/gnatbind" "--GNATLINK=@out@/bin/gnatlink ")
 fi
 
-# Add the flags that should be passed to the linker (and prevent
-# `ld-wrapper' from adding NIX_LDFLAGS again).
-#for i in $NIX_LDFLAGS_BEFORE; do
-#    extraBefore=(${extraBefore[@]} "-largs $i")
-#done
+#if [ "$dontLink" != 1 ]; then
+#    # Add the flags that should be passed to the linker (and prevent
+#    # `ld-wrapper' from adding NIX_@infixSalt@_LDFLAGS again).
+#    for i in $NIX_@infixSalt@_LDFLAGS_BEFORE; do
+#        extraBefore+=("-largs" "$i")
+#    done
+#    for i in $NIX_@infixSalt@_LDFLAGS; do
+#        if [ "${i:0:3}" = -L/ ]; then
+#            extraAfter+=("$i")
+#        else
+#            extraAfter+=("-largs" "$i")
+#        fi
+#    done
+#    export NIX_@infixSalt@_LDFLAGS_SET=1
+#fi
 
 # Optionally print debug info.
-if [ -n "$NIX_DEBUG" ]; then
-  echo "original flags to @prog@:" >&2
-  for i in "${params[@]}"; do
-      echo "  $i" >&2
-  done
-  echo "extraBefore flags to @prog@:" >&2
-  for i in ${extraBefore[@]}; do
-      echo "  $i" >&2
-  done
-  echo "extraAfter flags to @prog@:" >&2
-  for i in ${extraAfter[@]}; do
-      echo "  $i" >&2
-  done
+if [ -n "${NIX_DEBUG:-}" ]; then
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" "${extraBefore[@]}"  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" "${params[@]}" >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" "${extraAfter[@]}" >&2
 fi
 
-if [ -n "$NIX_GNAT_WRAPPER_EXEC_HOOK" ]; then
-    source "$NIX_GNAT_WRAPPER_EXEC_HOOK"
+if [ -n "$NIX_@infixSalt@_GNAT_WRAPPER_EXEC_HOOK" ]; then
+    source "$NIX_@infixSalt@_GNAT_WRAPPER_EXEC_HOOK"
 fi
 
 PATH="$path_backup"
-exec @prog@ ${extraBefore[@]} "${params[@]}" ${extraAfter[@]}
+exec @prog@ "${extraBefore[@]}" "${params[@]}" "${extraAfter[@]}"
diff --git a/pkgs/build-support/cc-wrapper/gnatlink-wrapper.sh b/pkgs/build-support/cc-wrapper/gnatlink-wrapper.sh
index c9958dbbb413..ee973d3270f9 100644
--- a/pkgs/build-support/cc-wrapper/gnatlink-wrapper.sh
+++ b/pkgs/build-support/cc-wrapper/gnatlink-wrapper.sh
@@ -1,33 +1,40 @@
-#! @shell@ -e
+#! @shell@
+set -eu -o pipefail
+shopt -s nullglob
+
+# N.B. Gnat is not used during bootstrapping, so we don't need to
+# worry about the old bash empty array `set -u` workarounds.
 
 # Add the flags for the GNAT compiler proper.
-extraAfter="--GCC=@out@/bin/gcc"
+extraAfter=("--GCC=@out@/bin/gcc")
 extraBefore=()
 
-# Add the flags that should be passed to the linker (and prevent
-# `ld-wrapper' from adding NIX_LDFLAGS again).
-#for i in $NIX_LDFLAGS_BEFORE; do
-#    extraBefore=(${extraBefore[@]} "-largs $i")
+## Add the flags that should be passed to the linker (and prevent
+## `ld-wrapper' from adding NIX_@infixSalt@_LDFLAGS again).
+#for i in $NIX_@infixSalt@_LDFLAGS_BEFORE; do
+#    extraBefore+=("-largs" "$i")
+#done
+#for i in $NIX_@infixSalt@_LDFLAGS; do
+#    if [ "${i:0:3}" = -L/ ]; then
+#        extraAfter+=("$i")
+#    else
+#        extraAfter+=("-largs" "$i")
+#    fi
 #done
+#export NIX_@infixSalt@_LDFLAGS_SET=1
 
 # Optionally print debug info.
-if [ -n "$NIX_DEBUG" ]; then
-  echo "original flags to @prog@:" >&2
-  for i in "$@"; do
-      echo "  $i" >&2
-  done
-  echo "extraBefore flags to @prog@:" >&2
-  for i in ${extraBefore[@]}; do
-      echo "  $i" >&2
-  done
-  echo "extraAfter flags to @prog@:" >&2
-  for i in ${extraAfter[@]}; do
-      echo "  $i" >&2
-  done
+if [ -n "${NIX_DEBUG:-}" ]; then
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" "${extraBefore[@]}"  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" "$@" >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" "${extraAfter[@]}" >&2
 fi
 
-if [ -n "$NIX_GNAT_WRAPPER_EXEC_HOOK" ]; then
-    source "$NIX_GNAT_WRAPPER_EXEC_HOOK"
+if [ -n "$NIX_@infixSalt@_GNAT_WRAPPER_EXEC_HOOK" ]; then
+    source "$NIX_@infixSalt@_GNAT_WRAPPER_EXEC_HOOK"
 fi
 
-exec @prog@ ${extraBefore[@]} "$@" ${extraAfter[@]}
+exec @prog@ "${extraBefore[@]}" "$@" "${extraAfter[@]}"
diff --git a/pkgs/build-support/cc-wrapper/ld-solaris-wrapper.sh b/pkgs/build-support/cc-wrapper/ld-solaris-wrapper.sh
index 263ea5408e9a..72c999ff8bc8 100755
--- a/pkgs/build-support/cc-wrapper/ld-solaris-wrapper.sh
+++ b/pkgs/build-support/cc-wrapper/ld-solaris-wrapper.sh
@@ -1,40 +1,25 @@
 #!@shell@
+set -eu -o pipefail
+shopt -s nullglob
 
-set -e
-set -u
-
+declare -a args=("$@")
 # I've also tried adding -z direct and -z lazyload, but it gave too many problems with C++ exceptions :'(
 # Also made sure libgcc would not be lazy-loaded, as suggested here: https://www.illumos.org/issues/2534#note-3
 #   but still no success.
-cmd="@ld@ -z ignore"
-
-args=("$@");
+declare -a argsBefore=(-z ignore) argsAfter=()
 
 # This loop makes sure all -L arguments are before -l arguments, or ld may complain it cannot find a library.
 # GNU binutils does not have this problem:
 #   http://stackoverflow.com/questions/5817269/does-the-order-of-l-and-l-options-in-the-gnu-linker-matter
-i=0;
-while [[ $i -lt $# ]]; do
+while (( $# )); do
     case "${args[$i]}" in
-        -L)  cmd="$cmd ${args[$i]} ${args[($i+1)]}"; i=($i+1); ;;
-        -L*) cmd="$cmd ${args[$i]}" ;;
-        *)   ;;
+        -L)   argsBefore+=("$1" "$2"); shift ;;
+        -L?*) argsBefore+=("$1") ;;
+        *)    argsAfter+=("$1") ;;
     esac
-    i=($i+1);
-done
-
-i=0;
-while [[ $i -lt $# ]]; do
-    case "${args[$i]}" in
-        -L)  i=($i+1); ;;
-        -L*) ;;
-        *)   cmd="$cmd ${args[$i]}" ;;
-    esac
-    i=($i+1);
+    shift
 done
 
 # Trace:
 set -x
-exec $cmd
-
-exit 0
+exec "@ld@" "${argsBefore[@]}" "${argsAfter[@]}"
diff --git a/pkgs/build-support/cc-wrapper/ld-wrapper.sh b/pkgs/build-support/cc-wrapper/ld-wrapper.sh
index 056cfa920535..d0a1d5a0ddb7 100644
--- a/pkgs/build-support/cc-wrapper/ld-wrapper.sh
+++ b/pkgs/build-support/cc-wrapper/ld-wrapper.sh
@@ -1,15 +1,21 @@
-#! @shell@ -e
+#! @shell@
+set -eu -o pipefail
+shopt -s nullglob
+
 path_backup="$PATH"
+
+# phase separation makes this look useless
+# shellcheck disable=SC2157
 if [ -n "@coreutils_bin@" ]; then
-  PATH="@coreutils_bin@/bin"
+    PATH="@coreutils_bin@/bin"
 fi
 
-if [ -n "$NIX_LD_WRAPPER_START_HOOK" ]; then
-    source "$NIX_LD_WRAPPER_START_HOOK"
+if [ -z "${NIX_CC_WRAPPER_@infixSalt@_FLAGS_SET:-}" ]; then
+    source @out@/nix-support/add-flags.sh
 fi
 
-if [ -z "$NIX_CC_WRAPPER_FLAGS_SET" ]; then
-    source @out@/nix-support/add-flags.sh
+if [ -n "$NIX_LD_WRAPPER_@infixSalt@_START_HOOK" ]; then
+    source "$NIX_LD_WRAPPER_@infixSalt@_START_HOOK"
 fi
 
 source @out@/nix-support/utils.sh
@@ -17,21 +23,22 @@ source @out@/nix-support/utils.sh
 
 # Optionally filter out paths not refering to the store.
 expandResponseParams "$@"
-if [ "$NIX_ENFORCE_PURITY" = 1 -a -n "$NIX_STORE" \
-        -a \( -z "$NIX_IGNORE_LD_THROUGH_GCC" -o -z "$NIX_LDFLAGS_SET" \) ]; then
+if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "${NIX_STORE:-}"
+        && ( -z "$NIX_@infixSalt@_IGNORE_LD_THROUGH_GCC" || -z "${NIX_@infixSalt@_LDFLAGS_SET:-}" ) ]]; then
     rest=()
-    n=0
-    while [ $n -lt ${#params[*]} ]; do
+    nParams=${#params[@]}
+    declare -i n=0
+    while [ "$n" -lt "$nParams" ]; do
         p=${params[n]}
-        p2=${params[$((n+1))]}
+        p2=${params[n+1]:-} # handle `p` being last one
         if [ "${p:0:3}" = -L/ ] && badPath "${p:2}"; then
-            skip $p
+            skip "${p:2}"
         elif [ "$p" = -L ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         elif [ "$p" = -rpath ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         elif [ "$p" = -dynamic-linker ] && badPath "$p2"; then
-            n=$((n + 1)); skip $p2
+            n+=1; skip "$p2"
         elif [ "${p:0:1}" = / ] && badPath "$p"; then
             # We cannot skip this; barf.
             echo "impure path \`$p' used in link" >&2
@@ -40,149 +47,128 @@ if [ "$NIX_ENFORCE_PURITY" = 1 -a -n "$NIX_STORE" \
             # Our ld is not built with sysroot support (Can we fix that?)
             :
         else
-            rest=("${rest[@]}" "$p")
+            rest+=("$p")
         fi
-        n=$((n + 1))
+        n+=1
     done
     params=("${rest[@]}")
 fi
 
-LD=@prog@
 source @out@/nix-support/add-hardening.sh
 
-extra=(${hardeningLDFlags[@]})
+extraAfter=("${hardeningLDFlags[@]}")
 extraBefore=()
 
-if [ -z "$NIX_LDFLAGS_SET" ]; then
-    extra+=($NIX_LDFLAGS)
-    extraBefore+=($NIX_LDFLAGS_BEFORE)
+if [ -z "${NIX_@infixSalt@_LDFLAGS_SET:-}" ]; then
+    extraAfter+=($NIX_@infixSalt@_LDFLAGS)
+    extraBefore+=($NIX_@infixSalt@_LDFLAGS_BEFORE)
 fi
 
-extra+=($NIX_LDFLAGS_AFTER $NIX_LDFLAGS_HARDEN)
-
-
-# Add all used dynamic libraries to the rpath.
-if [ "$NIX_DONT_SET_RPATH" != 1 ]; then
-
-    libPath=""
-    addToLibPath() {
-        local path="$1"
-        if [ "${path:0:1}" != / ]; then return 0; fi
-        case "$path" in
-            *..*|*./*|*/.*|*//*)
-                local path2
-                if path2=$(readlink -f "$path"); then
-                    path="$path2"
-                fi
+extraAfter+=($NIX_@infixSalt@_LDFLAGS_AFTER $NIX_@infixSalt@_LDFLAGS_HARDEN)
+
+declare -a libDirs
+declare -A libs
+relocatable=
+
+# Find all -L... switches for rpath, and relocatable flags for build id.
+if [ "$NIX_@infixSalt@_DONT_SET_RPATH" != 1 ] || [ "$NIX_@infixSalt@_SET_BUILD_ID" = 1 ]; then
+    prev=
+    # Old bash thinks empty arrays are undefined, ugh, so temporarily disable
+    # `set -u`.
+    set +u
+    for p in "${extraBefore[@]}" "${params[@]}" "${extraAfter[@]}"; do
+        set -u
+        case "$prev" in
+            -L)
+                libDirs+=("$p")
+                ;;
+            -l)
+                libs["lib${p}.so"]=1
+                ;;
+            -dynamic-linker | -plugin)
+                # Ignore this argument, or it will match *.so and be added to rpath.
+                ;;
+            *)
+                case "$p" in
+                    -L/*)
+                        libDirs+=("${p:2}")
+                        ;;
+                    -l?*)
+                        libs["lib${p:2}.so"]=1
+                        ;;
+                    "${NIX_STORE:-}"/*.so | "${NIX_STORE:-}"/*.so.*)
+                        # This is a direct reference to a shared library.
+                        libDirs+=("${p%/*}")
+                        libs["${p##*/}"]=1
+                        ;;
+                    -r | --relocatable | -i)
+                        relocatable=1
+                esac
                 ;;
         esac
-        case $libPath in
-            *\ $path\ *) return 0 ;;
-        esac
-        libPath="$libPath $path "
-    }
-
-    addToRPath() {
-        # If the path is not in the store, don't add it to the rpath.
-        # This typically happens for libraries in /tmp that are later
-        # copied to $out/lib.  If not, we're screwed.
-        if [ "${1:0:${#NIX_STORE}}" != "$NIX_STORE" ]; then return 0; fi
-        case $rpath in
-            *\ $1\ *) return 0 ;;
-        esac
-        rpath="$rpath $1 "
-    }
-
-    libs=""
-    addToLibs() {
-        libs="$libs $1"
-    }
-
-    rpath=""
-
-    # First, find all -L... switches.
-    allParams=("${params[@]}" ${extra[@]})
-    n=0
-    while [ $n -lt ${#allParams[*]} ]; do
-        p=${allParams[n]}
-        p2=${allParams[$((n+1))]}
-        if [ "${p:0:3}" = -L/ ]; then
-            addToLibPath ${p:2}
-        elif [ "$p" = -L ]; then
-            addToLibPath ${p2}
-            n=$((n + 1))
-        elif [ "$p" = -l ]; then
-            addToLibs ${p2}
-            n=$((n + 1))
-        elif [ "${p:0:2}" = -l ]; then
-            addToLibs ${p:2}
-        elif [ "$p" = -dynamic-linker ]; then
-            # Ignore the dynamic linker argument, or it
-            # will get into the next 'elif'. We don't want
-            # the dynamic linker path rpath to go always first.
-            n=$((n + 1))
-        elif [[ "$p" =~ ^[^-].*\.so($|\.) ]]; then
-            # This is a direct reference to a shared library, so add
-            # its directory to the rpath.
-            path="$(dirname "$p")";
-            addToRPath "${path}"
-        fi
-        n=$((n + 1))
+        prev="$p"
     done
+fi
+
 
-    # Second, for each directory in the library search path (-L...),
+# Add all used dynamic libraries to the rpath.
+if [ "$NIX_@infixSalt@_DONT_SET_RPATH" != 1 ]; then
+    # For each directory in the library search path (-L...),
     # see if it contains a dynamic library used by a -l... flag.  If
     # so, add the directory to the rpath.
     # It's important to add the rpath in the order of -L..., so
     # the link time chosen objects will be those of runtime linking.
-
-    for i in $libPath; do
-        for j in $libs; do
-            if [ -f "$i/lib$j.so" ]; then
-                addToRPath $i
+    declare -A rpaths
+    for dir in "${libDirs[@]}"; do
+        if [[ "$dir" =~ [/.][/.] ]] && dir2=$(readlink -f "$dir"); then
+            dir="$dir2"
+        fi
+        if [ -n "${rpaths[$dir]:-}" ] || [[ "$dir" != "${NIX_STORE:-}"/* ]]; then
+            # If the path is not in the store, don't add it to the rpath.
+            # This typically happens for libraries in /tmp that are later
+            # copied to $out/lib.  If not, we're screwed.
+            continue
+        fi
+        for path in "$dir"/*; do
+            file="${path##*/}"
+            if [ "${libs[$file]:-}" ]; then
+                # This library may have been provided by a previous directory,
+                # but if that library file is inside an output of the current
+                # derivation, it can be deleted after this compilation and
+                # should be found in a later directory, so we add all
+                # directories that contain any of the libraries to rpath.
+                rpaths["$dir"]=1
+                extraAfter+=(-rpath "$dir")
                 break
             fi
         done
     done
-
-
-    # Finally, add `-rpath' switches.
-    for i in $rpath; do
-        extra+=(-rpath $i)
-    done
 fi
 
 
 # Only add --build-id if this is a final link. FIXME: should build gcc
 # with --enable-linker-build-id instead?
-if [ "$NIX_SET_BUILD_ID" = 1 ]; then
-    for p in "${params[@]}"; do
-        if [ "$p" = "-r" -o "$p" = "--relocatable" -o "$p" = "-i" ]; then
-            relocatable=1
-            break
-        fi
-    done
-    if [ -z "$relocatable" ]; then
-        extra+=(--build-id)
-    fi
+if [ "$NIX_@infixSalt@_SET_BUILD_ID" = 1 ] && [ ! "$relocatable" ]; then
+    extraAfter+=(--build-id)
 fi
 
 
 # Optionally print debug info.
-if [ -n "$NIX_DEBUG" ]; then
-  echo "original flags to @prog@:" >&2
-  for i in "${params[@]}"; do
-      echo "  $i" >&2
-  done
-  echo "extra flags to @prog@:" >&2
-  for i in ${extra[@]}; do
-      echo "  $i" >&2
-  done
+if [ -n "${NIX_DEBUG:-}" ]; then
+    set +u # Old bash workaround, see above.
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" "${extraBefore[@]}"  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" "${params[@]}" >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" "${extraAfter[@]}" >&2
+    set -u
 fi
 
-if [ -n "$NIX_LD_WRAPPER_EXEC_HOOK" ]; then
-    source "$NIX_LD_WRAPPER_EXEC_HOOK"
+if [ -n "$NIX_LD_WRAPPER_@infixSalt@_EXEC_HOOK" ]; then
+    source "$NIX_LD_WRAPPER_@infixSalt@_EXEC_HOOK"
 fi
 
 PATH="$path_backup"
-exec @prog@ ${extraBefore[@]} "${params[@]}" ${extra[@]}
+set +u # Old bash workaround, see above.
+exec @prog@ "${extraBefore[@]}" "${params[@]}" "${extraAfter[@]}"
diff --git a/pkgs/build-support/cc-wrapper/macos-sierra-reexport-hack.bash b/pkgs/build-support/cc-wrapper/macos-sierra-reexport-hack.bash
new file mode 100644
index 000000000000..b7aa7ea5c092
--- /dev/null
+++ b/pkgs/build-support/cc-wrapper/macos-sierra-reexport-hack.bash
@@ -0,0 +1,106 @@
+#! @shell@
+
+set -eu -o pipefail
+
+path_backup="$PATH"
+if [ -n "@coreutils_bin@" ]; then
+  PATH="@coreutils_bin@/bin"
+fi
+
+declare -r recurThreshold=300
+
+declare overflowCount=0
+for ((n=0; n < $#; ++n)); do
+    case "${!n}" in
+        -l*) let overflowCount+=1 ;;
+        -reexport-l*) let overflowCount+=1 ;;
+        *) ;;
+    esac
+done
+
+declare -a allArgs=()
+
+if (( "$overflowCount" <= "$recurThreshold" )); then
+    allArgs=("$@")
+else
+    declare -a childrenLookup=() childrenLink=()
+
+    while (( $# )); do
+        case "$1" in
+            -L/*)
+                childrenLookup+=("$1")
+                allArgs+=("$1")
+                ;;
+            -L)
+                echo "cctools LD does not support '-L foo' or '-l foo'" >&2
+                exit 1
+                ;;
+            -l)
+                echo "cctools LD does not support '-L foo' or '-l foo'" >&2
+                exit 1
+                ;;
+            -lazy_library | -lazy_framework | -lto_library)
+                # We aren't linking any "azy_library", "to_library", etc.
+                allArgs+=("$1")
+                ;;
+            -lazy-l | -weak-l)    allArgs+=("$1") ;;
+                # We can't so easily prevent header issues from these.
+            -lSystem)             allArgs+=("$1") ;;
+                # Special case as indirection seems like a bad idea for something
+                # so fundamental. Can be removed for simplicity.
+            -l?* | -reexport-l?*) childrenLink+=("$1") ;;
+            *)                    allArgs+=("$1") ;;
+        esac
+
+        shift
+    done
+
+    declare n=0
+    while (( $n < "${#childrenLink[@]}" )); do
+        if [[ "${childrenLink[n]}" = -l* ]]; then
+            childrenLink[n]="-reexport${childrenLink[n]}"
+        fi
+        let ++n
+    done
+    unset n
+
+    declare -r outputNameLibless=$(basename $( \
+        if [[ -z "${outputName:+isUndefined}" ]]; then
+            echo unnamed
+        elif [[ "${outputName:0:3}" = lib ]]; then
+            echo "${outputName:3}"
+        else
+            echo "${outputName}"
+        fi))
+    declare -ra children=("$outputNameLibless-reexport-delegate-0" \
+                          "$outputNameLibless-reexport-delegate-1")
+
+    mkdir -p "$out/lib"
+
+    PATH="$PATH:@out@/bin"
+
+    symbolBloatObject=$outputNameLibless-symbol-hack.o
+    if [[ ! -e $symbolBloatObject ]]; then
+        printf '.private_extern _______child_hack_foo\nchild_hack_foo:\n' \
+            | @binPrefix@as -- -o $symbolBloatObject
+    fi
+
+    # first half of libs
+    @binPrefix@ld -macosx_version_min $MACOSX_DEPLOYMENT_TARGET -arch x86_64 -dylib \
+      -o "$out/lib/lib${children[0]}.dylib" \
+      -install_name "$out/lib/lib${children[0]}.dylib" \
+      "${childrenLookup[@]}" "$symbolBloatObject" \
+      "${childrenLink[@]:0:$((${#childrenLink[@]} / 2 ))}"
+
+    # second half of libs
+    @binPrefix@ld -macosx_version_min $MACOSX_DEPLOYMENT_TARGET -arch x86_64 -dylib \
+      -o "$out/lib/lib${children[1]}.dylib" \
+      -install_name "$out/lib/lib${children[1]}.dylib" \
+      "${childrenLookup[@]}" "$symbolBloatObject" \
+      "${childrenLink[@]:$((${#childrenLink[@]} / 2 ))}"
+
+    allArgs+=("-L$out/lib" "-l${children[0]}" "-l${children[1]}")
+fi
+
+PATH="$path_backup"
+exec @prog@ "${allArgs[@]}"
diff --git a/pkgs/build-support/cc-wrapper/setup-hook.sh b/pkgs/build-support/cc-wrapper/setup-hook.sh
index f4f7ab181d3e..c6abd6281d26 100644
--- a/pkgs/build-support/cc-wrapper/setup-hook.sh
+++ b/pkgs/build-support/cc-wrapper/setup-hook.sh
@@ -1,47 +1,153 @@
-export NIX_CC=@out@
+# CC Wrapper hygiene
+#
+# For at least cross compilation, we need to depend on multiple cc-wrappers at
+# once---specifically up to one per sort of dependency. This follows from having
+# different tools targeting different platforms, and different flags for those
+# tools. For example:
+#
+#   # Flags for compiling (whether or not linking) C code for the...
+#   NIX_BUILD_CFLAGS_COMPILE  # ...build platform
+#   NIX_CFLAGS_COMPILE        # ...host platform
+#   NIX_TARGET_CFLAGS_COMPILE # ...target platform
+#
+# Notice that these platforms are the 3 *relative* to the package using
+# cc-wrapper, not absolute like `x86_64-pc-linux-gnu`.
+#
+# The simplest solution would be to have separate cc-wrappers per (3 intended
+# use-cases * n absolute concrete platforms). For the use-case axis, we would
+# @-splice in 'BUILD_' '' 'TARGET_' to use the write environment variables when
+# building the cc-wrapper, and likewise prefix the binaries' names so they didn't
+# clobber each other on the PATH. But the need for 3x cc-wrappers, along with
+# non-standard name prefixes, is annoying and liable to break packages' build
+# systems.
+#
+# Instead, we opt to have just one cc-wrapper per absolute platform. Matching
+# convention, the binaries' names can just be prefixed with their target
+# platform. On the other hand, that means packages will depend on not just
+# multiple cc-wrappers, but the exact same cc-wrapper derivation multiple ways.
+# That means the exact same cc-wrapper derivation must be able to avoid
+# conflicting with itself, despite the fact that `setup-hook.sh`, the `addCvars`
+# function, and `add-flags.sh` are all communicating with each other with
+# environment variables. Yuck.
+#
+# The basic strategy is:
+#
+#  - Everyone exclusively *adds information* to relative-platform-specific
+#    environment variables, like `NIX_TARGET_CFLAGS_COMPILE`, to communicate
+#    with the wrapped binaries.
+#
+#  - The wrapped binaries will exclusively *read* cc-wrapper-derivation-specific
+#    environment variables distinguished with with `infixSalt`, like
+#    `NIX_@infixSalt@_CFLAGS_COMPILE`.
+#
+#  - `add-flags`, beyond its old task of reading extra flags stuck inside the
+#    cc-wrapper derivation, will convert the relative-platform-specific
+#    variables to cc-wrapper-derivation-specific variables. This conversion is
+#    the only time all but one of the cc-wrapper-derivation-specific variables
+#    are set.
+#
+# This ensures the flow of information is exclusive from
+# relative-platform-specific variables to cc-wrapper-derivation-specific
+# variables. This allows us to support the general case of a many--many relation
+# between relative platforms and cc-wrapper derivations.
+#
+# For more details, read the individual files where the mechanisms used to
+# accomplish this will be individually documented.
 
-addCVars () {
-    if [ -d $1/include ]; then
-        export NIX_CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include"
+
+# It's fine that any other cc-wrapper will redefine this. Bash functions close
+# over no state, and there's no @-substitutions within, so any redefined
+# function is guaranteed to be exactly the same.
+ccWrapper_addCVars () {
+    # The `depOffset` describes how the platforms of the dependencies are slid
+    # relative to the depending package. It is brought into scope of the
+    # environment hook defined as the role of the dependency being applied.
+    case $depOffset in
+        -1) local role='BUILD_' ;;
+        0)  local role='' ;;
+        1)  local role='TARGET_' ;;
+        *)  echo "cc-wrapper: Error: Cannot be used with $depOffset-offset deps, " >2;
+            return 1 ;;
+    esac
+
+    if [[ -d "$1/include" ]]; then
+        export NIX_${role}CFLAGS_COMPILE+=" ${ccIncludeFlag:--isystem} $1/include"
     fi
 
-    if [ -d $1/lib64 -a ! -L $1/lib64 ]; then
-        export NIX_LDFLAGS+=" -L$1/lib64"
+    if [[ -d "$1/lib64" && ! -L "$1/lib64" ]]; then
+        export NIX_${role}LDFLAGS+=" -L$1/lib64"
     fi
 
-    if [ -d $1/lib ]; then
-        export NIX_LDFLAGS+=" -L$1/lib"
+    if [[ -d "$1/lib" ]]; then
+        export NIX_${role}LDFLAGS+=" -L$1/lib"
     fi
 
-    if test -d $1/Library/Frameworks; then
-        export NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -F$1/Library/Frameworks"
+    if [[ -d "$1/Library/Frameworks" ]]; then
+        export NIX_${role}CFLAGS_COMPILE+=" -F$1/Library/Frameworks"
     fi
 }
 
-envHooks+=(addCVars)
+# Since the same cc-wrapper derivation can be depend on in multiple ways, we
+# need to accumulate *each* role (i.e. target platform relative the depending
+# derivation) in which the cc-wrapper derivation is used.
+# `NIX_CC_WRAPPER_@infixSalt@_TARGET_*` tracks this (needs to be an exported env
+# var so can't use fancier data structures).
+#
+# We also need to worry about what role is being added on *this* invocation of
+# setup-hook, which `role` tracks.
+if [ -n "${crossConfig:-}" ]; then
+    export NIX_CC_WRAPPER_@infixSalt@_TARGET_BUILD=1
+    role="BUILD_"
+else
+    export NIX_CC_WRAPPER_@infixSalt@_TARGET_HOST=1
+    role=""
+fi
+
+# Eventually the exact sort of env-hook we create will depend on the role. This
+# is because based on what relative platform we are targeting, we use different
+# dependencies.
+envHooks+=(ccWrapper_addCVars)
 
-# Note: these come *after* $out in the PATH (see setup.sh).
+# Note 1: these come *after* $out in the PATH (see setup.sh).
+# Note 2: phase separation makes this look useless to shellcheck.
 
+# shellcheck disable=SC2157
 if [ -n "@cc@" ]; then
     addToSearchPath _PATH @cc@/bin
 fi
 
+# shellcheck disable=SC2157
 if [ -n "@binutils_bin@" ]; then
     addToSearchPath _PATH @binutils_bin@/bin
 fi
 
+# shellcheck disable=SC2157
 if [ -n "@libc_bin@" ]; then
     addToSearchPath _PATH @libc_bin@/bin
 fi
 
+# shellcheck disable=SC2157
 if [ -n "@coreutils_bin@" ]; then
     addToSearchPath _PATH @coreutils_bin@/bin
 fi
 
-if [ -z "$crossConfig" ]; then
-    export CC=@real_cc@
-    export CXX=@real_cxx@
-else
-    export BUILD_CC=@real_cc@
-    export BUILD_CXX=@real_cxx@
-fi
+# Export tool environment variables so various build systems use the right ones.
+
+export NIX_${role}CC=@out@
+
+export ${role}CC=@named_cc@
+export ${role}CXX=@named_cxx@
+
+for CMD in \
+    cpp \
+    ar as nm objcopy ranlib strip strings size ld windres
+do
+    if
+        PATH=$_PATH type -p "@binPrefix@$CMD" > /dev/null
+    then
+        export "${role}$(echo "$CMD" | tr "[:lower:]" "[:upper:]")=@binPrefix@${CMD}";
+    fi
+done
+
+# No local scope in sourced file
+unset role
diff --git a/pkgs/build-support/cc-wrapper/utils.sh b/pkgs/build-support/cc-wrapper/utils.sh
index 87e48da9c8d5..5a70c2d9ccf3 100644
--- a/pkgs/build-support/cc-wrapper/utils.sh
+++ b/pkgs/build-support/cc-wrapper/utils.sh
@@ -1,5 +1,5 @@
 skip () {
-    if [ -n "$NIX_DEBUG" ]; then
+    if [ -n "${NIX_DEBUG:-}" ]; then
         echo "skipping impure path $1" >&2
     fi
 }
@@ -24,11 +24,15 @@ badPath() {
 }
 
 expandResponseParams() {
-    params=("$@")
+    declare -g params=("$@")
     local arg
     for arg in "$@"; do
         if [[ "$arg" == @* ]]; then
+            # phase separation makes this look useless
+            # shellcheck disable=SC2157
             if [ -n "@expandResponseParams@" ]; then
+                # params is used by caller
+                #shellcheck disable=SC2034
                 readarray -d '' params < <("@expandResponseParams@" "$@")
                 return 0
             else
diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix
index 506ef7837a2e..17d7f2da035c 100644
--- a/pkgs/build-support/docker/default.nix
+++ b/pkgs/build-support/docker/default.nix
@@ -6,6 +6,7 @@
   findutils,
   go,
   jshon,
+  jq,
   lib,
   pkgs,
   pigz,
@@ -408,9 +409,10 @@ rec {
                   contents runAsRoot diskSize extraCommands;
         };
       result = runCommand "docker-image-${baseName}.tar.gz" {
-        buildInputs = [ jshon pigz coreutils findutils ];
-        imageName = name;
-        imageTag = tag;
+        buildInputs = [ jshon pigz coreutils findutils jq ];
+        # Image name and tag must be lowercase
+        imageName = lib.toLower name;
+        imageTag = lib.toLower tag;
         inherit fromImage baseJson;
         layerClosure = writeReferencesToFile layer;
         passthru.buildArgs = args;
@@ -434,6 +436,9 @@ rec {
         if [[ -n "$fromImage" ]]; then
           echo "Unpacking base image..."
           tar -C image -xpf "$fromImage"
+          # Do not import the base image configuration and manifest
+          chmod a+w image image/*.json
+          rm -f image/*.json
 
           if [[ -z "$fromImageName" ]]; then
             fromImageName=$(jshon -k < image/repositories|head -n1)
@@ -492,6 +497,24 @@ rec {
         # Use the temp folder we've been working on to create a new image.
         mv temp image/$layerID
 
+        # Create image json and image manifest
+        imageJson=$(cat ${baseJson} | jq ". + {\"rootfs\": {\"diff_ids\": [], \"type\": \"layers\"}}")
+        manifestJson=$(jq -n "[{\"RepoTags\":[\"$imageName:$imageTag\"]}]")
+        currentID=$layerID
+        while [[ -n "$currentID" ]]; do
+          layerChecksum=$(sha256sum image/$currentID/layer.tar | cut -d ' ' -f1)
+          imageJson=$(echo "$imageJson" | jq ".history |= [{\"created\": \"${created}\"}] + .")
+          imageJson=$(echo "$imageJson" | jq ".rootfs.diff_ids |= [\"sha256:$layerChecksum\"] + .")
+          manifestJson=$(echo "$manifestJson" | jq ".[0].Layers |= [\"$currentID/layer.tar\"] + .")
+
+          currentID=$(cat image/$currentID/json | (jshon -e parent -u 2>/dev/null || true))
+        done
+
+        imageJsonChecksum=$(echo "$imageJson" | sha256sum | cut -d ' ' -f1)
+        echo "$imageJson" > "image/$imageJsonChecksum.json"
+        manifestJson=$(echo "$manifestJson" | jq ".[0].Config = \"$imageJsonChecksum.json\"")
+        echo "$manifestJson" > image/manifest.json
+
         # Store the json under the name image/repositories.
         jshon -n object \
           -n object -s "$layerID" -i "$imageTag" \
@@ -501,7 +524,7 @@ rec {
         chmod -R a-w image
 
         echo "Cooking the image..."
-        tar -C image --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 -c . | pigz -nT > $out
+        tar -C image --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --xform s:'./':: -c . | pigz -nT > $out
 
         echo "Finished."
       '';
diff --git a/pkgs/build-support/emacs/wrapper.nix b/pkgs/build-support/emacs/wrapper.nix
index e41b1fd6a215..bd733f1b9baf 100644
--- a/pkgs/build-support/emacs/wrapper.nix
+++ b/pkgs/build-support/emacs/wrapper.nix
@@ -80,7 +80,8 @@ stdenv.mkDerivation {
        linkPath "$1" "share/emacs/site-lisp" "share/emacs/site-lisp"
      }
 
-     for pkg in $requires; do
+     # Iterate over the array of inputs (avoiding nix's own interpolation)
+     for pkg in "''${requires[@]}"; do
        linkEmacsPackage $pkg
      done
 
diff --git a/pkgs/build-support/fetchbower/default.nix b/pkgs/build-support/fetchbower/default.nix
index 835fbec6bf0e..dd0bac49cb6d 100644
--- a/pkgs/build-support/fetchbower/default.nix
+++ b/pkgs/build-support/fetchbower/default.nix
@@ -7,8 +7,10 @@ let
       ver = if builtins.length components == 1 then version else hash;
     in ver;
 
+  bowerName = name: lib.replaceStrings ["/"] ["-"] name;
+
   fetchbower = name: version: target: outputHash: stdenv.mkDerivation {
-    name = "${name}-${bowerVersion version}";
+    name = "${bowerName name}-${bowerVersion version}";
     SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
     buildCommand = ''
       fetch-bower --quiet --out=$PWD/out "${name}" "${target}" "${version}"
diff --git a/pkgs/build-support/fetchpatch/default.nix b/pkgs/build-support/fetchpatch/default.nix
index a6ddf132cd5b..a9bfac320fb8 100644
--- a/pkgs/build-support/fetchpatch/default.nix
+++ b/pkgs/build-support/fetchpatch/default.nix
@@ -5,7 +5,7 @@
 # stripLen acts as the -p parameter when applying a patch.
 
 { lib, fetchurl, patchutils }:
-{ stripLen ? 0, addPrefixes ? false, ... }@args:
+{ stripLen ? 0, addPrefixes ? false, excludes ? [], ... }@args:
 
 fetchurl ({
   postFetch = ''
@@ -21,7 +21,10 @@ fetchurl ({
            --addnewprefix=b/ \
         ''} \
         --clean "$out" > "$tmpfile"
-    mv "$tmpfile" "$out"
+    ${patchutils}/bin/filterdiff \
+      -p1 \
+      ${builtins.toString (builtins.map (x: "-x ${x}") excludes)} \
+      "$tmpfile" > "$out"
     ${args.postFetch or ""}
   '';
-} // builtins.removeAttrs args ["stripLen" "addPrefixes"])
+} // builtins.removeAttrs args ["stripLen" "addPrefixes" "excludes"])
diff --git a/pkgs/build-support/fetchurl/boot.nix b/pkgs/build-support/fetchurl/boot.nix
index 722fd2566ef3..bd71f93c5291 100644
--- a/pkgs/build-support/fetchurl/boot.nix
+++ b/pkgs/build-support/fetchurl/boot.nix
@@ -5,10 +5,11 @@ let mirrors = import ./mirrors.nix; in
 { url ? builtins.head urls
 , urls ? []
 , sha256
+, name ? baseNameOf (toString url)
 }:
 
 import <nix/fetchurl.nix> {
-  inherit system sha256;
+  inherit system sha256 name;
 
   url =
     # Handle mirror:// URIs. Since <nix/fetchurl.nix> currently
diff --git a/pkgs/build-support/fetchurl/builder.sh b/pkgs/build-support/fetchurl/builder.sh
index c4fd18e46caf..7c2bdf260b4e 100644
--- a/pkgs/build-support/fetchurl/builder.sh
+++ b/pkgs/build-support/fetchurl/builder.sh
@@ -39,7 +39,6 @@ tryDownload() {
           curlexit=$?;
        fi
     done
-    stopNest
 }
 
 
@@ -51,7 +50,6 @@ finish() {
     fi
 
     runHook postFetch
-    stopNest
     exit 0
 }
 
diff --git a/pkgs/build-support/gcc-wrapper-old/builder.sh b/pkgs/build-support/gcc-wrapper-old/builder.sh
index a8e8a370ec0d..22e32814927e 100644
--- a/pkgs/build-support/gcc-wrapper-old/builder.sh
+++ b/pkgs/build-support/gcc-wrapper-old/builder.sh
@@ -211,5 +211,5 @@ cp -p $utils $out/nix-support/utils.sh
 # tools like gcov, the manpages, etc. as well (including for binutils
 # and Glibc).
 if test -z "$nativeTools"; then
-    echo $gcc $binutils $libc $libc_bin > $out/nix-support/propagated-user-env-packages
+    printWords $gcc $binutils $libc $libc_bin > $out/nix-support/propagated-user-env-packages
 fi
diff --git a/pkgs/build-support/setup-hooks/die.sh b/pkgs/build-support/setup-hooks/die.sh
new file mode 100644
index 000000000000..0db41e030f4c
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/die.sh
@@ -0,0 +1,21 @@
+# Exit with backtrace and error message
+#
+# Usage: die "Error message"
+die() {
+    # Let us be a little sloppy with errors, because otherwise the final
+    # invocation of `caller` below will cause the script to exit.
+    set +e
+
+    # Print our error message
+    printf "\nBuilder called die: %b\n" "$*"
+    printf "Backtrace:\n"
+
+    # Print a backtrace.
+    local frame=0
+    while caller $frame; do
+        ((frame++));
+    done
+    printf "\n"
+
+    exit 1
+}
diff --git a/pkgs/build-support/setup-hooks/make-wrapper.sh b/pkgs/build-support/setup-hooks/make-wrapper.sh
index eebde886a884..cde28fbbcaf1 100644
--- a/pkgs/build-support/setup-hooks/make-wrapper.sh
+++ b/pkgs/build-support/setup-hooks/make-wrapper.sh
@@ -1,3 +1,12 @@
+# Assert that FILE exists and is executable
+#
+# assertExecutable FILE
+assertExecutable() {
+    local file="$1"
+    [[ -f "${file}" && -x "${file}" ]] || \
+        die "Cannot wrap ${file} because it is not an executable file"
+}
+
 # construct an executable file that wraps the actual executable
 # makeWrapper EXECUTABLE ARGS
 
@@ -24,6 +33,8 @@ makeWrapper() {
     local params varName value command separator n fileNames
     local argv0 flagsBefore flags
 
+    assertExecutable "${original}"
+
     mkdir -p "$(dirname "$wrapper")"
 
     echo "#! $SHELL -e" > "$wrapper"
@@ -32,26 +43,20 @@ makeWrapper() {
     for ((n = 2; n < ${#params[*]}; n += 1)); do
         p="${params[$n]}"
 
-        if test "$p" = "--set"; then
+        if [[ "$p" == "--set" ]]; then
             varName="${params[$((n + 1))]}"
             value="${params[$((n + 2))]}"
             n=$((n + 2))
             echo "export $varName=\"$value\"" >> "$wrapper"
-        fi
-
-        if test "$p" = "--unset"; then
+        elif [[ "$p" == "--unset" ]]; then
             varName="${params[$((n + 1))]}"
             n=$((n + 1))
             echo "unset $varName" >> "$wrapper"
-        fi
-
-        if test "$p" = "--run"; then
+        elif [[ "$p" == "--run" ]]; then
             command="${params[$((n + 1))]}"
             n=$((n + 1))
             echo "$command" >> "$wrapper"
-        fi
-
-        if test "$p" = "--suffix" -o "$p" = "--prefix"; then
+        elif [[ ("$p" == "--suffix") || ("$p" == "--prefix") ]]; then
             varName="${params[$((n + 1))]}"
             separator="${params[$((n + 2))]}"
             value="${params[$((n + 3))]}"
@@ -63,9 +68,7 @@ makeWrapper() {
                     echo "export $varName=$value\${$varName:+$separator}\$$varName" >> "$wrapper"
                 fi
             fi
-        fi
-
-        if test "$p" = "--suffix-each"; then
+        elif [[ "$p" == "--suffix-each" ]]; then
             varName="${params[$((n + 1))]}"
             separator="${params[$((n + 2))]}"
             values="${params[$((n + 3))]}"
@@ -73,9 +76,7 @@ makeWrapper() {
             for value in $values; do
                 echo "export $varName=\$$varName\${$varName:+$separator}$value" >> "$wrapper"
             done
-        fi
-
-        if test "$p" = "--suffix-contents" -o "$p" = "--prefix-contents"; then
+        elif [[ ("$p" == "--suffix-contents") || ("$p" == "--prefix-contents") ]]; then
             varName="${params[$((n + 1))]}"
             separator="${params[$((n + 2))]}"
             fileNames="${params[$((n + 3))]}"
@@ -87,17 +88,15 @@ makeWrapper() {
                     echo "export $varName=$(cat "$fileName")\${$varName:+$separator}\$$varName" >> "$wrapper"
                 fi
             done
-        fi
-
-        if test "$p" = "--add-flags"; then
+        elif [[ "$p" == "--add-flags" ]]; then
             flags="${params[$((n + 1))]}"
             n=$((n + 1))
             flagsBefore="$flagsBefore $flags"
-        fi
-
-        if test "$p" = "--argv0"; then
+        elif [[ "$p" == "--argv0" ]]; then
             argv0="${params[$((n + 1))]}"
             n=$((n + 1))
+        else
+            die "makeWrapper doesn't understand the arg $p"
         fi
     done
 
@@ -131,6 +130,9 @@ filterExisting() {
 wrapProgram() {
     local prog="$1"
     local hidden
+
+    assertExecutable "${prog}"
+
     hidden="$(dirname "$prog")/.$(basename "$prog")"-wrapped
     while [ -e "$hidden" ]; do
       hidden="${hidden}_"
@@ -138,5 +140,5 @@ wrapProgram() {
     mv "$prog" "$hidden"
     # Silence warning about unexpanded $0:
     # shellcheck disable=SC2016
-    makeWrapper "$hidden" "$prog" --argv0 '$0' "$@"
+    makeWrapper "$hidden" "$prog" --argv0 '$0' "${@:2}"
 }
diff --git a/pkgs/build-support/setup-hooks/setup-debug-info-dirs.sh b/pkgs/build-support/setup-hooks/setup-debug-info-dirs.sh
new file mode 100644
index 000000000000..2fd2a2d6da6f
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/setup-debug-info-dirs.sh
@@ -0,0 +1,5 @@
+setupDebugInfoDirs () {
+    addToSearchPath NIX_DEBUG_INFO_DIRS $1/lib/debug
+}
+
+envHooks+=(setupDebugInfoDirs)
diff --git a/pkgs/build-support/setup-hooks/wrap-gapps-hook.sh b/pkgs/build-support/setup-hooks/wrap-gapps-hook.sh
index 9891128a6231..79b8d5b73fa1 100644
--- a/pkgs/build-support/setup-hooks/wrap-gapps-hook.sh
+++ b/pkgs/build-support/setup-hooks/wrap-gapps-hook.sh
@@ -39,7 +39,7 @@ wrapGAppsHook() {
     targetDirs=( "${prefix}/bin" "${prefix}/libexec" )
     for targetDir in "${targetDirs[@]}"; do
       if [[ -d "${targetDir}" ]]; then
-        find "${targetDir}" -type f -executable -print0 \
+        find -L "${targetDir}" -type f -executable -print0 \
           | while IFS= read -r -d '' file; do
           echo "Wrapping program ${file}"
           wrapProgram "${file}" "${gappsWrapperArgs[@]}"
diff --git a/pkgs/build-support/trivial-builders.nix b/pkgs/build-support/trivial-builders.nix
index 4debd9636396..14553c33e039 100644
--- a/pkgs/build-support/trivial-builders.nix
+++ b/pkgs/build-support/trivial-builders.nix
@@ -26,6 +26,7 @@ rec {
     , text
     , executable ? false # run chmod +x ?
     , destination ? ""   # relative path appended to $out eg "/bin/foo"
+    , checkPhase ? ""    # syntax checks, e.g. for scripts
     }:
     runCommand name
       { inherit text executable;
@@ -44,6 +45,8 @@ rec {
           echo -n "$text" > "$n"
         fi
 
+        ${checkPhase}
+
         (test -n "$executable" && chmod +x "$n") || true
       '';
 
@@ -54,6 +57,20 @@ rec {
   writeScript = name: text: writeTextFile {inherit name text; executable = true;};
   writeScriptBin = name: text: writeTextFile {inherit name text; executable = true; destination = "/bin/${name}";};
 
+  # Create a Shell script, check its syntax
+  writeShellScriptBin = name : text :
+    writeTextFile {
+      inherit name;
+      executable = true;
+      destination = "/bin/${name}";
+      text = ''
+        #!${stdenv.shell}
+        ${text}
+        '';
+      checkPhase = ''
+        ${stdenv.shell} -n $out
+      '';
+    };
 
   # Create a forest of symlinks to the files in `paths'.
   symlinkJoin =
@@ -84,7 +101,7 @@ rec {
         mkdir -p $out/nix-support
         cp ${script} $out/nix-support/setup-hook
       '' + lib.optionalString (deps != []) ''
-        echo ${toString deps} > $out/nix-support/propagated-native-build-inputs
+        printWords ${toString deps} > $out/nix-support/propagated-native-build-inputs
       '' + lib.optionalString (substitutions != {}) ''
         substituteAll ${script} $out/nix-support/setup-hook
       '');
diff --git a/pkgs/build-support/vm/default.nix b/pkgs/build-support/vm/default.nix
index d5cfc419fc72..d886e9a56fa4 100644
--- a/pkgs/build-support/vm/default.nix
+++ b/pkgs/build-support/vm/default.nix
@@ -750,6 +750,7 @@ rec {
     { name, fullName, size ? 4096, urlPrefix
     , packagesList ? "", packagesLists ? [packagesList]
     , packages, extraPackages ? [], postInstall ? ""
+    , extraDebs ? []
     , QEMU_OPTS ? "", memSize ? 512 }:
 
     let
@@ -760,7 +761,7 @@ rec {
     in
       (fillDiskWithDebs {
         inherit name fullName size postInstall QEMU_OPTS memSize;
-        debs = import expr {inherit fetchurl;};
+        debs = import expr {inherit fetchurl;} ++ extraDebs;
       }) // {inherit expr;};
 
 
@@ -1954,22 +1955,22 @@ rec {
     };
 
     debian8i386 = {
-      name = "debian-8.8-jessie-i386";
-      fullName = "Debian 8.8 Jessie (i386)";
+      name = "debian-8.9-jessie-i386";
+      fullName = "Debian 8.9 Jessie (i386)";
       packagesList = fetchurl {
         url = mirror://debian/dists/jessie/main/binary-i386/Packages.xz;
-        sha256 = "79dbf81e9698913c577333f47f5a56be78529fba265ec492880e8c369c478b58";
+        sha256 = "3c78bdf3b693f2f37737c52d6a7718b3a545956f2a853da79f04a2d15541e811";
       };
       urlPrefix = mirror://debian;
       packages = commonDebianPackages;
     };
 
     debian8x86_64 = {
-      name = "debian-8.8-jessie-amd64";
-      fullName = "Debian 8.8 Jessie (amd64)";
+      name = "debian-8.9-jessie-amd64";
+      fullName = "Debian 8.9 Jessie (amd64)";
       packagesList = fetchurl {
         url = mirror://debian/dists/jessie/main/binary-amd64/Packages.xz;
-        sha256 = "845fc80c9934d8c0f78ada6455c81c331a3359ef15c4c036b47e742fb1bb99c6";
+        sha256 = "0605589ae7a63c690f37bd2567dc12e02a2eb279d9dc200a7310072ad3593e53";
       };
       urlPrefix = mirror://debian;
       packages = commonDebianPackages;