about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/bintools-wrapper
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support/bintools-wrapper')
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/add-darwin-ldflags-before.sh81
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/add-flags.sh37
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/add-hardening.sh62
-rwxr-xr-xnixpkgs/pkgs/build-support/bintools-wrapper/darwin-install_name_tool-wrapper.sh49
-rwxr-xr-xnixpkgs/pkgs/build-support/bintools-wrapper/darwin-strip-wrapper.sh78
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/default.nix440
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/gnu-binutils-strip-wrapper.sh4
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/ld-solaris-wrapper.sh29
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/ld-wrapper.sh273
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/macos-sierra-reexport-hack.bash246
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/setup-hook.sh72
11 files changed, 1371 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/add-darwin-ldflags-before.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/add-darwin-ldflags-before.sh
new file mode 100644
index 000000000000..75d9484846a8
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/add-darwin-ldflags-before.sh
@@ -0,0 +1,81 @@
+# Unconditionally adding in platform version flags will result in warnings that
+# will be treated as errors by some packages. Add any missing flags here.
+
+# There are two things to be configured: the "platform version" (oldest
+# supported version of macos, ios, etc), and the "sdk version".
+#
+# The modern way of configuring these is to use:
+#    -platform_version $platform $platform_version $sdk_version"
+#
+# The old way is still supported, and uses flags like:
+#    -${platform}_version_min $platform_version
+#    -sdk_version $sdk_version
+#
+# If both styles are specified ld will combine them. If multiple versions are
+# specified for the same platform, ld will emit an error.
+#
+# The following adds flags for whichever properties have not already been
+# provided.
+
+havePlatformVersionFlag=
+haveDarwinSDKVersion=
+haveDarwinPlatformVersion=
+
+# Roles will set by add-flags.sh, but add-flags.sh can be skipped when the
+# cc-wrapper has added the linker flags. Both the cc-wrapper and the binutils
+# wrapper mangle the same variable (MACOSX_DEPLOYMENT_TARGET), so if roles are
+# empty due to being run through the cc-wrapper then the mangle here is a no-op
+# and we still do the right thing.
+#
+# To be robust, make sure we always have the correct set of roles.
+accumulateRoles
+
+mangleVarSingle @darwinMinVersionVariable@ ${role_suffixes[@]+"${role_suffixes[@]}"}
+
+n=0
+nParams=${#params[@]}
+while (( n < nParams )); do
+    p=${params[n]}
+    case "$p" in
+        # the current platform
+        -@darwinPlatform@_version_min)
+            haveDarwinPlatformVersion=1
+            ;;
+
+        # legacy aliases
+        -macosx_version_min|-iphoneos_version_min|-iosmac_version_min|-uikitformac_version_min)
+            haveDarwinPlatformVersion=1
+            ;;
+
+        -sdk_version)
+            haveDarwinSDKVersion=1
+            ;;
+
+        -platform_version)
+            havePlatformVersionFlag=1
+
+            # If clang can't determine the sdk version it will pass 0.0.0. This
+            # has runtime effects so we override this to use the known sdk
+            # version.
+            if [ "${params[n+3]-}" = 0.0.0 ]; then
+                params[n+3]=@darwinSdkVersion@
+            fi
+            ;;
+    esac
+    n=$((n + 1))
+done
+
+# If the caller has set -platform_version, trust they're doing the right thing.
+# This will be the typical case for clang in nixpkgs.
+if [ ! "$havePlatformVersionFlag" ]; then
+    if [ ! "$haveDarwinSDKVersion" ] && [ ! "$haveDarwinPlatformVersion" ]; then
+        # Nothing provided. Use the modern "-platform_version" to set both.
+        extraBefore+=(-platform_version @darwinPlatform@ "${@darwinMinVersionVariable@_@suffixSalt@:-@darwinMinVersion@}" @darwinSdkVersion@)
+    elif [ ! "$haveDarwinSDKVersion" ]; then
+        # Add missing sdk version
+        extraBefore+=(-sdk_version @darwinSdkVersion@)
+    elif [ ! "$haveDarwinPlatformVersion" ]; then
+        # Add missing platform version
+        extraBefore+=(-@darwinPlatform@_version_min "${@darwinMinVersionVariable@_@suffixSalt@:-@darwinMinVersion@}")
+    fi
+fi
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/add-flags.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/add-flags.sh
new file mode 100644
index 000000000000..3b94daba65d7
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/add-flags.sh
@@ -0,0 +1,37 @@
+# See cc-wrapper for comments.
+var_templates_list=(
+    NIX_IGNORE_LD_THROUGH_GCC
+    NIX_LDFLAGS
+    NIX_LDFLAGS_BEFORE
+    NIX_DYNAMIC_LINKER
+    NIX_LDFLAGS_AFTER
+    NIX_LDFLAGS_HARDEN
+    NIX_HARDENING_ENABLE
+)
+var_templates_bool=(
+    NIX_SET_BUILD_ID
+    NIX_DONT_SET_RPATH
+)
+
+accumulateRoles
+
+for var in "${var_templates_list[@]}"; do
+    mangleVarList "$var" ${role_suffixes[@]+"${role_suffixes[@]}"}
+done
+for var in "${var_templates_bool[@]}"; do
+    mangleVarBool "$var" ${role_suffixes[@]+"${role_suffixes[@]}"}
+done
+
+if [ -e @out@/nix-support/libc-ldflags ]; then
+    NIX_LDFLAGS_@suffixSalt@+=" $(< @out@/nix-support/libc-ldflags)"
+fi
+
+if [ -z "$NIX_DYNAMIC_LINKER_@suffixSalt@" ] && [ -e @out@/nix-support/ld-set-dynamic-linker ]; then
+    NIX_DYNAMIC_LINKER_@suffixSalt@="$(< @out@/nix-support/dynamic-linker)"
+fi
+
+if [ -e @out@/nix-support/libc-ldflags-before ]; then
+    NIX_LDFLAGS_BEFORE_@suffixSalt@="$(< @out@/nix-support/libc-ldflags-before) $NIX_LDFLAGS_BEFORE_@suffixSalt@"
+fi
+
+export NIX_BINTOOLS_WRAPPER_FLAGS_SET_@suffixSalt@=1
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/add-hardening.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/add-hardening.sh
new file mode 100644
index 000000000000..db9553c3fc76
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/add-hardening.sh
@@ -0,0 +1,62 @@
+declare -a hardeningLDFlags=()
+
+declare -A hardeningEnableMap=()
+
+# Intentionally word-split in case 'NIX_HARDENING_ENABLE' is defined in Nix. The
+# array expansion also prevents undefined variables from causing trouble with
+# `set -u`.
+for flag in ${NIX_HARDENING_ENABLE_@suffixSalt@-}; do
+  hardeningEnableMap["$flag"]=1
+done
+
+# Remove unsupported flags.
+for flag in @hardening_unsupported_flags@; do
+  unset -v "hardeningEnableMap[$flag]"
+done
+
+if (( "${NIX_DEBUG:-0}" >= 1 )); then
+  declare -a allHardeningFlags=(pie relro bindnow)
+  declare -A hardeningDisableMap=()
+
+  # Determine which flags were effectively disabled so we can report below.
+  for flag in "${allHardeningFlags[@]}"; do
+    if [[ -z "${hardeningEnableMap[$flag]-}" ]]; then
+      hardeningDisableMap[$flag]=1
+    fi
+  done
+
+  printf 'HARDENING: disabled flags:' >&2
+  (( "${#hardeningDisableMap[@]}" )) && printf ' %q' "${!hardeningDisableMap[@]}" >&2
+  echo >&2
+
+  if (( "${#hardeningEnableMap[@]}" )); then
+    echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2;
+  fi
+fi
+
+for flag in "${!hardeningEnableMap[@]}"; do
+  case $flag in
+    pie)
+      if [[ ! (" ${params[*]} " =~ " -shared " \
+            || " ${params[*]} " =~ " -static " \
+            || " ${params[*]} " =~ " -r " \
+            || " ${params[*]} " =~ " -Ur " \
+            || " ${params[*]} " =~ " -i ") ]]; then
+        if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi
+        hardeningLDFlags+=('-pie')
+      fi
+      ;;
+    relro)
+      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling relro >&2; fi
+      hardeningLDFlags+=('-z' 'relro')
+      ;;
+    bindnow)
+      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling bindnow >&2; fi
+      hardeningLDFlags+=('-z' 'now')
+      ;;
+    *)
+      # Ignore unsupported. Checked in Nix that at least *some*
+      # tool supports each flag.
+      ;;
+  esac
+done
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-install_name_tool-wrapper.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-install_name_tool-wrapper.sh
new file mode 100755
index 000000000000..376a7abfe41c
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-install_name_tool-wrapper.sh
@@ -0,0 +1,49 @@
+#! @shell@
+# shellcheck shell=bash
+
+set -eu -o pipefail +o posix
+shopt -s nullglob
+
+if (( "${NIX_DEBUG:-0}" >= 7 )); then
+    set -x
+fi
+
+source @signingUtils@
+
+extraAfter=()
+extraBefore=()
+params=("$@")
+
+input=
+
+pprev=
+prev=
+for p in \
+    ${extraBefore+"${extraBefore[@]}"} \
+    ${params+"${params[@]}"} \
+    ${extraAfter+"${extraAfter[@]}"}
+do
+    if [ "$pprev" != "-change" ] && [[ "$prev" != -* ]] && [[ "$p" != -* ]]; then
+        input="$p"
+    fi
+    pprev="$prev"
+    prev="$p"
+done
+
+# Optionally print debug info.
+if (( "${NIX_DEBUG:-0}" >= 1 )); then
+    # Old bash workaround, see above.
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" ${extraBefore+"${extraBefore[@]}"}  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" ${params+"${params[@]}"} >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" ${extraAfter+"${extraAfter[@]}"} >&2
+fi
+
+@prog@ \
+    ${extraBefore+"${extraBefore[@]}"} \
+    ${params+"${params[@]}"} \
+    ${extraAfter+"${extraAfter[@]}"}
+
+sign "$input"
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-strip-wrapper.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-strip-wrapper.sh
new file mode 100755
index 000000000000..a67699547a6f
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/darwin-strip-wrapper.sh
@@ -0,0 +1,78 @@
+#! @shell@
+# shellcheck shell=bash
+
+set -eu -o pipefail +o posix
+shopt -s nullglob
+
+if (( "${NIX_DEBUG:-0}" >= 7 )); then
+    set -x
+fi
+
+source @signingUtils@
+
+extraAfter=()
+extraBefore=()
+params=("$@")
+
+output=
+inputs=()
+
+restAreFiles=
+prev=
+for p in \
+    ${extraBefore+"${extraBefore[@]}"} \
+    ${params+"${params[@]}"} \
+    ${extraAfter+"${extraAfter[@]}"}
+do
+    if [ "$restAreFiles" ]; then
+        inputs+=("$p")
+    else
+        case "$prev" in
+            -s|-R|-d|-arch)
+                # Unrelated arguments with values
+                ;;
+            -o)
+                # Explicit output
+                output="$p"
+                ;;
+            *)
+                # Any other orgument either takes no value, or is a file.
+                if [[ "$p" != -* ]]; then
+                    inputs+=("$p")
+                fi
+                ;;
+        esac
+
+        if [ "$p" == - ]; then
+            restAreFiles=1
+        fi
+    fi
+
+    prev="$p"
+done
+
+# Optionally print debug info.
+if (( "${NIX_DEBUG:-0}" >= 1 )); then
+    # Old bash workaround, see above.
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" ${extraBefore+"${extraBefore[@]}"}  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" ${params+"${params[@]}"} >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" ${extraAfter+"${extraAfter[@]}"} >&2
+fi
+
+@prog@ \
+    ${extraBefore+"${extraBefore[@]}"} \
+    ${params+"${params[@]}"} \
+    ${extraAfter+"${extraAfter[@]}"}
+
+if [ "$output" ]; then
+    # Single explicit output
+    signIfRequired "$output"
+else
+    # Multiple inputs, rewritten in place
+    for input in "${inputs[@]}"; do
+      signIfRequired "$input"
+    done
+fi
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix b/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix
new file mode 100644
index 000000000000..2d75330f1c9e
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix
@@ -0,0 +1,440 @@
+# The Nixpkgs CC is not directly usable, since it doesn't know where
+# the C library and standard header files are. Therefore the compiler
+# produced by that package cannot be installed directly in a user
+# environment and used from the command line. So we use a wrapper
+# script that sets up the right environment variables so that the
+# compiler and the linker just "work".
+
+{ name ? ""
+, lib
+, stdenvNoCC
+, runtimeShell
+, bintools ? null, libc ? null, coreutils ? null, gnugrep ? null
+, netbsd ? null
+, sharedLibraryLoader ?
+  if libc == null then
+    null
+  else if stdenvNoCC.targetPlatform.isNetBSD then
+    if !(targetPackages ? netbsd) then
+      netbsd.ld_elf_so
+    else if libc != targetPackages.netbsd.headers then
+      targetPackages.netbsd.ld_elf_so
+    else
+      null
+  else
+    lib.getLib libc
+, nativeTools, noLibc ? false, nativeLibc, nativePrefix ? ""
+, propagateDoc ? bintools != null && bintools ? man
+, extraPackages ? [], extraBuildCommands ? ""
+, isGNU ? bintools.isGNU or false
+, isLLVM ? bintools.isLLVM or false
+, isCCTools ? bintools.isCCTools or false
+, expand-response-params
+, targetPackages ? {}
+, useMacosReexportHack ? false
+, wrapGas ? false
+
+# Note: the hardening flags are part of the bintools-wrapper, rather than
+# the cc-wrapper, because a few of them are handled by the linker.
+, defaultHardeningFlags ? [
+    "bindnow"
+    "format"
+    "fortify"
+    "fortify3"
+    "pic"
+    "relro"
+    "stackprotector"
+    "strictoverflow"
+  ] ++ lib.optional (with stdenvNoCC;
+    # Musl-based platforms will keep "pie", other platforms will not.
+    # If you change this, make sure to update section `{#sec-hardening-in-nixpkgs}`
+    # in the nixpkgs manual to inform users about the defaults.
+    targetPlatform.libc == "musl"
+    # Except when:
+    #    - static aarch64, where compilation works, but produces segfaulting dynamically linked binaries.
+    #    - static armv7l, where compilation fails.
+    && !(targetPlatform.isAarch && targetPlatform.isStatic)
+  ) "pie"
+
+# Darwin code signing support utilities
+, postLinkSignHook ? null, signingUtils ? null
+}:
+
+assert propagateDoc -> bintools ? man;
+assert nativeTools -> !propagateDoc && nativePrefix != "";
+assert !nativeTools -> bintools != null && coreutils != null && gnugrep != null;
+assert !(nativeLibc && noLibc);
+assert (noLibc || nativeLibc) == (libc == null);
+
+let
+  inherit (lib)
+    attrByPath
+    concatStringsSep
+    getBin
+    getDev
+    getLib
+    getName
+    getVersion
+    hasSuffix
+    optional
+    optionalAttrs
+    optionals
+    optionalString
+    platforms
+    removePrefix
+    replaceStrings
+    ;
+
+  inherit (stdenvNoCC) hostPlatform targetPlatform;
+
+  # Prefix for binaries. Customarily ends with a dash separator.
+  #
+  # TODO(@Ericson2314) Make unconditional, or optional but always true by
+  # default.
+  targetPrefix = optionalString (targetPlatform != hostPlatform)
+                                        (targetPlatform.config + "-");
+
+  bintoolsVersion = getVersion bintools;
+  bintoolsName = removePrefix targetPrefix (getName bintools);
+
+  libc_bin = optionalString (libc != null) (getBin libc);
+  libc_dev = optionalString (libc != null) (getDev libc);
+  libc_lib = optionalString (libc != null) (getLib libc);
+  bintools_bin = optionalString (!nativeTools) (getBin bintools);
+  # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
+  coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
+
+  # See description in cc-wrapper.
+  suffixSalt = replaceStrings ["-" "."] ["_" "_"] targetPlatform.config;
+
+  # The dynamic linker has different names on different platforms. This is a
+  # shell glob that ought to match it.
+  dynamicLinker =
+    /**/ if sharedLibraryLoader == null then ""
+    else if targetPlatform.libc == "musl"             then "${sharedLibraryLoader}/lib/ld-musl-*"
+    else if targetPlatform.libc == "uclibc"           then "${sharedLibraryLoader}/lib/ld*-uClibc.so.1"
+    else if (targetPlatform.libc == "bionic" && targetPlatform.is32bit) then "/system/bin/linker"
+    else if (targetPlatform.libc == "bionic" && targetPlatform.is64bit) then "/system/bin/linker64"
+    else if targetPlatform.libc == "nblibc"           then "${sharedLibraryLoader}/libexec/ld.elf_so"
+    else if targetPlatform.system == "i686-linux"     then "${sharedLibraryLoader}/lib/ld-linux.so.2"
+    else if targetPlatform.system == "x86_64-linux"   then "${sharedLibraryLoader}/lib/ld-linux-x86-64.so.2"
+    # ELFv1 (.1) or ELFv2 (.2) ABI
+    else if targetPlatform.isPower64                  then "${sharedLibraryLoader}/lib/ld64.so.*"
+    # ARM with a wildcard, which can be "" or "-armhf".
+    else if (with targetPlatform; isAarch32 && isLinux)   then "${sharedLibraryLoader}/lib/ld-linux*.so.3"
+    else if targetPlatform.system == "aarch64-linux"  then "${sharedLibraryLoader}/lib/ld-linux-aarch64.so.1"
+    else if targetPlatform.system == "powerpc-linux"  then "${sharedLibraryLoader}/lib/ld.so.1"
+    else if targetPlatform.isMips                     then "${sharedLibraryLoader}/lib/ld.so.1"
+    # `ld-linux-riscv{32,64}-<abi>.so.1`
+    else if targetPlatform.isRiscV                    then "${sharedLibraryLoader}/lib/ld-linux-riscv*.so.1"
+    else if targetPlatform.isLoongArch64              then "${sharedLibraryLoader}/lib/ld-linux-loongarch*.so.1"
+    else if targetPlatform.isDarwin                   then "/usr/lib/dyld"
+    else if targetPlatform.isFreeBSD                  then "${sharedLibraryLoader}/libexec/ld-elf.so.1"
+    else if hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
+    else "";
+
+in
+
+stdenvNoCC.mkDerivation {
+  pname = targetPrefix
+    + (if name != "" then name else "${bintoolsName}-wrapper");
+  version = optionalString (bintools != null) bintoolsVersion;
+
+  preferLocalBuild = true;
+
+  outputs = [ "out" ] ++ optionals propagateDoc ([ "man" ] ++ optional (bintools ? info) "info");
+
+  passthru = {
+    inherit targetPrefix suffixSalt;
+    inherit bintools libc nativeTools nativeLibc nativePrefix isGNU isLLVM;
+
+    emacsBufferSetup = pkgs: ''
+      ; We should handle propagation here too
+      (mapc
+        (lambda (arg)
+          (when (file-directory-p (concat arg "/lib"))
+            (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib")))
+          (when (file-directory-p (concat arg "/lib64"))
+            (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib64"))))
+        '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
+    '';
+
+    inherit defaultHardeningFlags;
+  };
+
+  dontBuild = true;
+  dontConfigure = true;
+
+  enableParallelBuilding = true;
+
+  unpackPhase = ''
+    src=$PWD
+  '';
+
+  installPhase =
+    ''
+      mkdir -p $out/bin $out/nix-support
+
+      wrap() {
+        local dst="$1"
+        local wrapper="$2"
+        export prog="$3"
+        export use_response_file_by_default=${if isCCTools then "1" else "0"}
+        substituteAll "$wrapper" "$out/bin/$dst"
+        chmod +x "$out/bin/$dst"
+      }
+    ''
+
+    + (if nativeTools then ''
+      echo ${nativePrefix} > $out/nix-support/orig-bintools
+
+      ldPath="${nativePrefix}/bin"
+    '' else ''
+      echo $bintools_bin > $out/nix-support/orig-bintools
+
+      ldPath="${bintools_bin}/bin"
+    ''
+
+    # Solaris needs an additional ld wrapper.
+    + optionalString (targetPlatform.isSunOS && nativePrefix != "") ''
+      ldPath="${nativePrefix}/bin"
+      exec="$ldPath/${targetPrefix}ld"
+      wrap ld-solaris ${./ld-solaris-wrapper.sh}
+    '')
+
+    # If we are asked to wrap `gas` and this bintools has it,
+    # then symlink it (`as` will be symlinked next).
+    # This is mainly for the wrapped gnat-bootstrap on x86-64 Darwin,
+    # as it must have both the GNU assembler from cctools (installed as `gas`)
+    # and the Clang integrated assembler (installed as `as`).
+    # See pkgs/os-specific/darwin/binutils/default.nix for details.
+    + optionalString wrapGas ''
+      if [ -e $ldPath/${targetPrefix}gas ]; then
+        ln -s $ldPath/${targetPrefix}gas $out/bin/${targetPrefix}gas
+      fi
+    ''
+
+    # Create symlinks for rest of the binaries.
+    + ''
+      for binary in objdump objcopy size strings as ar nm gprof dwp c++filt addr2line \
+          ranlib readelf elfedit dlltool dllwrap windmc windres; do
+        if [ -e $ldPath/${targetPrefix}''${binary} ]; then
+          ln -s $ldPath/${targetPrefix}''${binary} $out/bin/${targetPrefix}''${binary}
+        fi
+      done
+
+    '' + (if !useMacosReexportHack then ''
+      if [ -e ''${ld:-$ldPath/${targetPrefix}ld} ]; then
+        wrap ${targetPrefix}ld ${./ld-wrapper.sh} ''${ld:-$ldPath/${targetPrefix}ld}
+      fi
+    '' else ''
+      ldInner="${targetPrefix}ld-reexport-delegate"
+      wrap "$ldInner" ${./macos-sierra-reexport-hack.bash} ''${ld:-$ldPath/${targetPrefix}ld}
+      wrap "${targetPrefix}ld" ${./ld-wrapper.sh} "$out/bin/$ldInner"
+      unset ldInner
+    '') + ''
+
+      for variant in $ldPath/${targetPrefix}ld.*; do
+        basename=$(basename "$variant")
+        wrap $basename ${./ld-wrapper.sh} $variant
+      done
+    '';
+
+  strictDeps = true;
+  depsTargetTargetPropagated = extraPackages;
+
+  setupHooks = [
+    ../setup-hooks/role.bash
+    ./setup-hook.sh
+  ];
+
+  postFixup =
+    ##
+    ## General libc support
+    ##
+    optionalString (libc != null) (''
+      touch "$out/nix-support/libc-ldflags"
+      echo "-L${libc_lib}${libc.libdir or "/lib"}" >> $out/nix-support/libc-ldflags
+
+      echo "${libc_lib}" > $out/nix-support/orig-libc
+      echo "${libc_dev}" > $out/nix-support/orig-libc-dev
+    ''
+
+    ##
+    ## Dynamic linker support
+    ##
+    + optionalString (sharedLibraryLoader != null) ''
+      if [[ -z ''${dynamicLinker+x} ]]; then
+        echo "Don't know the name of the dynamic linker for platform '${targetPlatform.config}', so guessing instead." >&2
+        local dynamicLinker="${sharedLibraryLoader}/lib/ld*.so.?"
+      fi
+    ''
+
+    # Expand globs to fill array of options
+    + ''
+      dynamicLinker=($dynamicLinker)
+
+      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
+
+      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 optionalString (sharedLibraryLoader != null) ''
+          if [ -e ${sharedLibraryLoader}/lib/32/ld-linux.so.2 ]; then
+            echo ${sharedLibraryLoader}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
+          fi
+          touch $out/nix-support/ld-set-dynamic-linker
+        ''}
+      fi
+    '')
+
+    ##
+    ## User env support
+    ##
+
+    # Propagate the underling unwrapped bintools so that if you
+    # install the wrapper, you get tools like objdump (same for any
+    # binaries of libc).
+    + optionalString (!nativeTools) ''
+      printWords ${bintools_bin} ${optionalString (libc != null) libc_bin} > $out/nix-support/propagated-user-env-packages
+    ''
+
+    ##
+    ## Man page and info support
+    ##
+    + optionalString propagateDoc (''
+      ln -s ${bintools.man} $man
+    '' + optionalString (bintools ? info) ''
+      ln -s ${bintools.info} $info
+    '')
+
+    ##
+    ## Hardening support
+    ##
+
+    # some linkers on some platforms don't support specific -z flags
+    + ''
+      export hardening_unsupported_flags=""
+      if [[ "$($ldPath/${targetPrefix}ld -z now 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
+        hardening_unsupported_flags+=" bindnow"
+      fi
+      if [[ "$($ldPath/${targetPrefix}ld -z relro 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
+        hardening_unsupported_flags+=" relro"
+      fi
+    ''
+
+    + optionalString hostPlatform.isCygwin ''
+      hardening_unsupported_flags+=" pic"
+    ''
+
+    + optionalString (targetPlatform.isAvr || targetPlatform.isWindows) ''
+      hardening_unsupported_flags+=" relro bindnow"
+    ''
+
+    + optionalString (libc != null && targetPlatform.isAvr) ''
+      for isa in avr5 avr3 avr4 avr6 avr25 avr31 avr35 avr51 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack; do
+        echo "-L${getLib libc}/avr/lib/$isa" >> $out/nix-support/libc-cflags
+      done
+    ''
+
+    + optionalString targetPlatform.isDarwin ''
+      echo "-arch ${targetPlatform.darwinArch}" >> $out/nix-support/libc-ldflags
+    ''
+
+    ##
+    ## GNU specific extra strip flags
+    ##
+
+    # TODO(@sternenseemann): make a generic strip wrapper?
+    + optionalString (bintools.isGNU or false) ''
+      wrap ${targetPrefix}strip ${./gnu-binutils-strip-wrapper.sh} \
+        "${bintools_bin}/bin/${targetPrefix}strip"
+    ''
+
+    ###
+    ### Remove certain timestamps from final binaries
+    ###
+    + optionalString (targetPlatform.isDarwin && !(bintools.isGNU or false)) ''
+      echo "export ZERO_AR_DATE=1" >> $out/nix-support/setup-hook
+    ''
+
+    + ''
+      for flags in "$out/nix-support"/*flags*; do
+        substituteInPlace "$flags" --replace $'\n' ' '
+      done
+
+      substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
+      substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
+      substituteAll ${../wrapper-common/utils.bash} $out/nix-support/utils.bash
+    ''
+
+    ###
+    ### Ensure consistent LC_VERSION_MIN_MACOSX
+    ###
+    + optionalString targetPlatform.isDarwin (
+      let
+        inherit (targetPlatform)
+          darwinPlatform darwinSdkVersion
+          darwinMinVersion darwinMinVersionVariable;
+      in ''
+        export darwinPlatform=${darwinPlatform}
+        export darwinMinVersion=${darwinMinVersion}
+        export darwinSdkVersion=${darwinSdkVersion}
+        export darwinMinVersionVariable=${darwinMinVersionVariable}
+        substituteAll ${./add-darwin-ldflags-before.sh} $out/nix-support/add-local-ldflags-before.sh
+      ''
+    )
+
+    ##
+    ## Code signing on Apple Silicon
+    ##
+    + optionalString (targetPlatform.isDarwin && targetPlatform.isAarch64) ''
+      echo 'source ${postLinkSignHook}' >> $out/nix-support/post-link-hook
+
+      export signingUtils=${signingUtils}
+
+      wrap \
+        ${targetPrefix}install_name_tool \
+        ${./darwin-install_name_tool-wrapper.sh} \
+        "${bintools_bin}/bin/${targetPrefix}install_name_tool"
+
+      wrap \
+        ${targetPrefix}strip ${./darwin-strip-wrapper.sh} \
+        "${bintools_bin}/bin/${targetPrefix}strip"
+    ''
+
+    ##
+    ## Extra custom steps
+    ##
+    + extraBuildCommands;
+
+  env = {
+    # for substitution in utils.bash
+    # TODO(@sternenseemann): invent something cleaner than passing in "" in case of absence
+    expandResponseParams = "${expand-response-params}/bin/expand-response-params";
+    # TODO(@sternenseemann): rename env var via stdenv rebuild
+    shell = (getBin runtimeShell + runtimeShell.shellPath or "");
+    gnugrep_bin = optionalString (!nativeTools) gnugrep;
+    wrapperName = "BINTOOLS_WRAPPER";
+    inherit dynamicLinker targetPrefix suffixSalt coreutils_bin;
+    inherit bintools_bin libc_bin libc_dev libc_lib;
+    default_hardening_flags_str = builtins.toString defaultHardeningFlags;
+  };
+
+  meta =
+    let bintools_ = optionalAttrs (bintools != null) bintools; in
+    (optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta ["priority"])) //
+    { description =
+        attrByPath ["meta" "description"] "System binary utilities" bintools_
+        + " (wrapper script)";
+      priority = 10;
+  } // optionalAttrs useMacosReexportHack {
+    platforms = platforms.darwin;
+  };
+}
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/gnu-binutils-strip-wrapper.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/gnu-binutils-strip-wrapper.sh
new file mode 100644
index 000000000000..5b5136e3d14c
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/gnu-binutils-strip-wrapper.sh
@@ -0,0 +1,4 @@
+#! @shell@
+# shellcheck shell=bash
+
+exec @prog@ --enable-deterministic-archives "$@"
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/ld-solaris-wrapper.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/ld-solaris-wrapper.sh
new file mode 100644
index 000000000000..5d81e34a047f
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/ld-solaris-wrapper.sh
@@ -0,0 +1,29 @@
+#!@shell@
+set -eu -o pipefail
+shopt -s nullglob
+
+if (( "${NIX_DEBUG:-0}" >= 7 )); then
+    set -x
+fi
+
+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.
+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
+while (( $# )); do
+    case "${args[$i]}" in
+        -L)   argsBefore+=("$1" "$2"); shift ;;
+        -L?*) argsBefore+=("$1") ;;
+        *)    argsAfter+=("$1") ;;
+    esac
+    shift
+done
+
+# Trace:
+set -x
+exec "@ld@" "${argsBefore[@]}" "${argsAfter[@]}"
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/ld-wrapper.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/ld-wrapper.sh
new file mode 100644
index 000000000000..dcbe8a4c2494
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/ld-wrapper.sh
@@ -0,0 +1,273 @@
+#! @shell@
+set -eu -o pipefail +o posix
+shopt -s nullglob
+
+if (( "${NIX_DEBUG:-0}" >= 7 )); then
+    set -x
+fi
+
+path_backup="$PATH"
+
+# phase separation makes this look useless
+# shellcheck disable=SC2157
+if [ -n "@coreutils_bin@" ]; then
+    PATH="@coreutils_bin@/bin"
+fi
+
+source @out@/nix-support/utils.bash
+
+if [ -z "${NIX_BINTOOLS_WRAPPER_FLAGS_SET_@suffixSalt@:-}" ]; then
+    source @out@/nix-support/add-flags.sh
+fi
+
+
+# Optionally filter out paths not refering to the store.
+expandResponseParams "$@"
+
+# NIX_LINK_TYPE is set if ld has been called through our cc wrapper. We take
+# advantage of this to avoid both recalculating it, and also repeating other
+# processing cc wrapper has already done.
+if [[ -n "${NIX_LINK_TYPE_@suffixSalt@:-}" ]]; then
+    linkType=$NIX_LINK_TYPE_@suffixSalt@
+else
+    linkType=$(checkLinkType "${params[@]}")
+fi
+
+if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "${NIX_STORE:-}"
+        && ( -z "$NIX_IGNORE_LD_THROUGH_GCC_@suffixSalt@" || -z "${NIX_LINK_TYPE_@suffixSalt@:-}" ) ]]; then
+    rest=()
+    nParams=${#params[@]}
+    declare -i n=0
+
+    while (( "$n" < "$nParams" )); do
+        p=${params[n]}
+        p2=${params[n+1]:-} # handle `p` being last one
+        if [ "${p:0:3}" = -L/ ] && badPath "${p:2}"; then
+            skip "${p:2}"
+        elif [ "$p" = -L ] && badPath "$p2"; then
+            n+=1; skip "$p2"
+        elif [ "$p" = -rpath ] && badPath "$p2"; then
+            n+=1; skip "$p2"
+        elif [ "$p" = -dynamic-linker ] && badPath "$p2"; then
+            n+=1; skip "$p2"
+        elif [ "$p" = -syslibroot ] && [ $p2 == // ]; then
+            # When gcc is built on darwin --with-build-sysroot=/
+            # produces '-syslibroot //' linker flag. It's a no-op,
+            # which does not introduce impurities.
+            n+=1; skip "$p2"
+        elif [ "${p:0:10}" = /LIBPATH:/ ] && badPath "${p:9}"; then
+            reject "${p:9}"
+        # We need to not match LINK.EXE-style flags like
+        # /NOLOGO or /LIBPATH:/nix/store/foo
+        elif [[ $p =~ ^/[^:]*/ ]] && badPath "$p"; then
+            reject "$p"
+        elif [ "${p:0:9}" = --sysroot ]; then
+            # Our ld is not built with sysroot support (Can we fix that?)
+            :
+        else
+            rest+=("$p")
+        fi
+        n+=1
+    done
+    # Old bash empty array hack
+    params=(${rest+"${rest[@]}"})
+fi
+
+
+source @out@/nix-support/add-hardening.sh
+
+extraAfter=()
+extraBefore=(${hardeningLDFlags[@]+"${hardeningLDFlags[@]}"})
+
+if [ -z "${NIX_LINK_TYPE_@suffixSalt@:-}" ]; then
+    extraAfter+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_@suffixSalt@))
+    extraBefore+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_BEFORE_@suffixSalt@))
+
+    # By adding dynamic linker to extraBefore we allow the users set their
+    # own dynamic linker as NIX_LD_FLAGS will override earlier set flags
+    if [[ "$linkType" == dynamic && -n "$NIX_DYNAMIC_LINKER_@suffixSalt@" ]]; then
+        extraBefore+=("-dynamic-linker" "$NIX_DYNAMIC_LINKER_@suffixSalt@")
+    fi
+fi
+
+extraAfter+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_AFTER_@suffixSalt@))
+
+# These flags *must not* be pulled up to -Wl, flags, so they can't go in
+# add-flags.sh. They must always be set, so must not be disabled by
+# NIX_LDFLAGS_SET.
+if [ -e @out@/nix-support/add-local-ldflags-before.sh ]; then
+    source @out@/nix-support/add-local-ldflags-before.sh
+fi
+
+
+# Three tasks:
+#
+#   1. Find all -L... switches for rpath
+#
+#   2. Find relocatable flag for build id.
+#
+#   3. Choose 32-bit dynamic linker if needed
+declare -a libDirs
+declare -A libs
+declare -i relocatable=0 link32=0
+
+linkerOutput="a.out"
+
+if
+    [ "$NIX_DONT_SET_RPATH_@suffixSalt@" != 1 ] \
+        || [ "$NIX_SET_BUILD_ID_@suffixSalt@" = 1 ] \
+        || [ -e @out@/nix-support/dynamic-linker-m32 ]
+then
+    prev=
+    # Old bash thinks empty arrays are undefined, ugh.
+    for p in \
+        ${extraBefore+"${extraBefore[@]}"} \
+        ${params+"${params[@]}"} \
+        ${extraAfter+"${extraAfter[@]}"}
+    do
+        case "$prev" in
+            -L)
+                libDirs+=("$p")
+                ;;
+            -l)
+                libs["lib${p}.so"]=1
+                ;;
+            -m)
+                # Presumably only the last `-m` flag has any effect.
+                case "$p" in
+                    elf_i386) link32=1;;
+                    *)        link32=0;;
+                esac
+                ;;
+            -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
+        prev="$p"
+    done
+fi
+
+# Determine linkerOutput
+prev=
+for p in \
+    ${extraBefore+"${extraBefore[@]}"} \
+    ${params+"${params[@]}"} \
+    ${extraAfter+"${extraAfter[@]}"}
+do
+    case "$prev" in
+        -o)
+            # Informational for post-link-hook
+            linkerOutput="$p"
+            ;;
+        *)
+            ;;
+    esac
+    prev="$p"
+done
+
+if [[ "$link32" == "1" && "$linkType" == dynamic && -e "@out@/nix-support/dynamic-linker-m32" ]]; then
+    # We have an alternate 32-bit linker and we're producing a 32-bit ELF, let's
+    # use it.
+    extraAfter+=(
+        '-dynamic-linker'
+        "$(< @out@/nix-support/dynamic-linker-m32)"
+    )
+fi
+
+# Add all used dynamic libraries to the rpath.
+if [[ "$NIX_DONT_SET_RPATH_@suffixSalt@" != 1 && "$linkType" != static-pie ]]; 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.
+    declare -A rpaths
+    for dir in ${libDirs+"${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
+
+fi
+
+# This is outside the DONT_SET_RPATH branch because it's more targeted and we
+# usually want it (on Darwin) even if DONT_SET_RPATH is set.
+if [ -n "${NIX_COREFOUNDATION_RPATH:-}" ]; then
+  extraAfter+=(-rpath $NIX_COREFOUNDATION_RPATH)
+fi
+
+# Only add --build-id if this is a final link. FIXME: should build gcc
+# with --enable-linker-build-id instead?
+#
+# Note: `lld` interprets `--build-id` to mean `--build-id=fast`; GNU ld defaults
+# to SHA1.
+if [ "$NIX_SET_BUILD_ID_@suffixSalt@" = 1 ] && ! (( "$relocatable" )); then
+    extraAfter+=(--build-id="${NIX_BUILD_ID_STYLE:-sha1}")
+fi
+
+
+# Optionally print debug info.
+if (( "${NIX_DEBUG:-0}" >= 1 )); then
+    # Old bash workaround, see above.
+    echo "extra flags before to @prog@:" >&2
+    printf "  %q\n" ${extraBefore+"${extraBefore[@]}"}  >&2
+    echo "original flags to @prog@:" >&2
+    printf "  %q\n" ${params+"${params[@]}"} >&2
+    echo "extra flags after to @prog@:" >&2
+    printf "  %q\n" ${extraAfter+"${extraAfter[@]}"} >&2
+fi
+
+PATH="$path_backup"
+# Old bash workaround, see above.
+
+if (( "${NIX_LD_USE_RESPONSE_FILE:-@use_response_file_by_default@}" >= 1 )); then
+    @prog@ @<(printf "%q\n" \
+        ${extraBefore+"${extraBefore[@]}"} \
+        ${params+"${params[@]}"} \
+        ${extraAfter+"${extraAfter[@]}"})
+else
+    @prog@ \
+        ${extraBefore+"${extraBefore[@]}"} \
+        ${params+"${params[@]}"} \
+        ${extraAfter+"${extraAfter[@]}"}
+fi
+
+if [ -e "@out@/nix-support/post-link-hook" ]; then
+    source @out@/nix-support/post-link-hook
+fi
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/macos-sierra-reexport-hack.bash b/nixpkgs/pkgs/build-support/bintools-wrapper/macos-sierra-reexport-hack.bash
new file mode 100644
index 000000000000..255071adf681
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/macos-sierra-reexport-hack.bash
@@ -0,0 +1,246 @@
+#! @shell@
+
+set -eu -o pipefail
+
+# For cmd | while read; do ...; done
+shopt -s lastpipe
+
+path_backup="$PATH"
+if [ -n "@coreutils_bin@" ]; then
+  PATH="@coreutils_bin@/bin"
+fi
+
+declare -ri recurThreshold=200
+declare -i overflowCount=0
+
+declare -ar origArgs=("$@")
+
+# Throw away what we won't need
+declare -a parentArgs=()
+
+while (( $# )); do
+    case "$1" in
+        -l)
+            echo "cctools LD does not support '-l foo'" >&2
+            exit 1
+            ;;
+        -lazy_library | -reexport_library | -upward_library | -weak_library)
+            overflowCount+=1
+            shift 2
+            ;;
+        -l* | *.so.* | *.dylib | -lazy-l* | -reexport-l* | -upward-l* | -weak-l*)
+            overflowCount+=1
+            shift 1
+            ;;
+        *.a | *.o)
+            shift 1
+            ;;
+        -L | -F)
+            # Evidentally ld doesn't like using the child's RPATH, so it still
+            # needs these.
+            parentArgs+=("$1" "$2")
+            shift 2
+            ;;
+        -L?* | -F?*)
+            parentArgs+=("$1")
+            shift 1
+            ;;
+        -o)
+            outputName="$2"
+            parentArgs+=("$1" "$2")
+            shift 2
+            ;;
+        -install_name | -dylib_install_name | -dynamic-linker | -plugin)
+            parentArgs+=("$1" "$2")
+            shift 2
+            ;;
+        -rpath)
+            # Only an rpath to the child is needed, which we will add
+            shift 2
+            ;;
+        *)
+            if [[ -f "$1" ]]; then
+                # Propabably a non-standard object file like Haskell's
+                # `.dyn_o`. Skip it like other inputs
+                :
+            else
+                parentArgs+=("$1")
+            fi
+            shift 1
+            ;;
+    esac
+done
+
+
+
+if (( "$overflowCount" <= "$recurThreshold" )); then
+    if [ -n "${NIX_DEBUG:-}" ]; then
+        echo "ld-wrapper: Only ${overflowCount} inputs counted while ${recurThreshold} is the ceiling, linking normally. " >&2
+    fi
+    PATH="$path_backup"
+    exec @prog@ "${origArgs[@]}"
+fi
+
+
+
+if [ -n "${NIX_DEBUG:-}" ]; then
+    echo "ld-wrapper: ${overflowCount} inputs counted when ${recurThreshold} is the ceiling, inspecting further. " >&2
+fi
+
+# Collect the normalized linker input
+declare -a norm=()
+
+# Arguments are null-separated
+@prog@ --dump-normalized-lib-args "${origArgs[@]}" |
+    while IFS= read -r -d '' input; do
+        norm+=("$input")
+    done
+
+declare -i leafCount=0
+declare lastLeaf=''
+declare -a childrenInputs=() trailingInputs=()
+while (( "${#norm[@]}" )); do
+    case "${norm[0]}" in
+        -lazy_library | -upward_library)
+            # TODO(@Ericson2314): Don't do that, but intersperse children
+            # between such args.
+            echo "ld-wrapper: Warning: Potentially changing link order" >&2
+            trailingInputs+=("${norm[0]}" "${norm[1]}")
+            norm=("${norm[@]:2}")
+            ;;
+        -reexport_library | -weak_library)
+            childrenInputs+=("${norm[0]}" "${norm[1]}")
+            if [[ "${norm[1]}" != "$lastLeaf" ]]; then
+                leafCount+=1
+                lastLeaf="${norm[1]}"
+            fi
+            norm=("${norm[@]:2}")
+            ;;
+        *.so | *.dylib)
+            childrenInputs+=(-reexport_library "${norm[0]}")
+            if [[ "${norm[0]}" != "$lastLeaf" ]]; then
+                leafCount+=1
+                lastLeaf="${norm[0]}"
+            fi
+            norm=("${norm[@]:1}")
+            ;;
+        *.o | *.a)
+            # Don't delegate object files or static libs
+            parentArgs+=("${norm[0]}")
+            norm=("${norm[@]:1}")
+            ;;
+        *)
+            if [[ -f "${norm[0]}" ]]; then
+                # Propabably a non-standard object file. We'll let it by.
+                parentArgs+=("${norm[0]}")
+                norm=("${norm[@]:1}")
+            else
+                echo "ld-wrapper: Internal Error: Invalid normalized argument" >&2
+                exit 255
+            fi
+            ;;
+    esac
+done
+
+
+
+if (( "$leafCount" <= "$recurThreshold" )); then
+    if [ -n "${NIX_DEBUG:-}" ]; then
+        echo "ld-wrapper: Only ${leafCount} *dynamic* inputs counted while ${recurThreshold} is the ceiling, linking normally. " >&2
+    fi
+    PATH="$path_backup"
+    exec @prog@ "${origArgs[@]}"
+fi
+
+
+
+if [ -n "${NIX_DEBUG:-}" ]; then
+    echo "ld-wrapper: ${leafCount} *dynamic* inputs counted when ${recurThreshold} is the ceiling, delegating to children. " >&2
+fi
+
+declare -r outputNameLibless=$( \
+    if [[ -z "${outputName:+isUndefined}" ]]; then
+        echo unnamed
+        return 0;
+    fi
+    baseName=$(basename ${outputName})
+    if [[ "$baseName" = lib* ]]; then
+        baseName="${baseName:3}"
+    fi
+    echo "$baseName")
+
+declare -ra children=(
+    "$outputNameLibless-reexport-delegate-0"
+    "$outputNameLibless-reexport-delegate-1"
+)
+
+mkdir -p "$out/lib"
+
+symbolBloatObject=$outputNameLibless-symbol-hack.o
+if [[ ! -f $symbolBloatObject ]]; then
+    # `-Q` means use GNU Assembler rather than Clang, avoiding an awkward
+    # dependency cycle.
+    printf '.private_extern _______child_hack_foo\nchild_hack_foo:\n' |
+        PATH="$PATH:@out@/bin" @targetPrefix@as -Q -- -o $symbolBloatObject
+fi
+
+# Split inputs between children
+declare -a child0Inputs=() child1Inputs=("${childrenInputs[@]}")
+let "countFirstChild = $leafCount / 2" || true
+lastLeaf=''
+while (( "$countFirstChild" )); do
+    case "${child1Inputs[0]}" in
+        -reexport_library | -weak_library)
+            child0Inputs+=("${child1Inputs[0]}" "${child1Inputs[1]}")
+            if [[ "${child1Inputs[1]}" != "$lastLeaf" ]]; then
+                let countFirstChild-=1 || true
+                lastLeaf="${child1Inputs[1]}"
+            fi
+            child1Inputs=("${child1Inputs[@]:2}")
+            ;;
+        *.so | *.dylib)
+            child0Inputs+=(-reexport_library "${child1Inputs[0]}")
+            if [[ "${child1Inputs[0]}" != "$lastLeaf" ]]; then
+                let countFirstChild-=1 || true
+                lastLeaf="${child1Inputs[1]}"
+            fi
+            child1Inputs=("${child1Inputs[@]:2}")
+            ;;
+        *)
+            echo "ld-wrapper: Internal Error: Invalid delegated input" >&2
+            exit -1
+            ;;
+    esac
+done
+
+
+# First half of libs
+@out@/bin/@targetPrefix@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" \
+  "$symbolBloatObject" "${child0Inputs[@]}" "${trailingInputs[@]}"
+
+# Second half of libs
+@out@/bin/@targetPrefix@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" \
+  "$symbolBloatObject" "${child1Inputs[@]}" "${trailingInputs[@]}"
+
+parentArgs+=("-L$out/lib" -rpath "$out/lib")
+if [[ $outputName != *reexport-delegate* ]]; then
+	parentArgs+=("-l${children[0]}" "-l${children[1]}")
+else
+    parentArgs+=("-reexport-l${children[0]}" "-reexport-l${children[1]}")
+fi
+
+parentArgs+=("${trailingInputs[@]}")
+
+if [ -n "${NIX_DEBUG:-}" ]; then
+    echo "flags using delegated children to @prog@:" >&2
+    printf "  %q\n" "${parentArgs[@]}" >&2
+fi
+
+PATH="$path_backup"
+exec @prog@ "${parentArgs[@]}"
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/setup-hook.sh b/nixpkgs/pkgs/build-support/bintools-wrapper/setup-hook.sh
new file mode 100644
index 000000000000..c146cbea80e4
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/setup-hook.sh
@@ -0,0 +1,72 @@
+# Binutils Wrapper hygiene
+#
+# See comments in cc-wrapper's setup hook. This works exactly the same way.
+
+# Skip setup hook if we're neither a build-time dep, nor, temporarily, doing a
+# native compile.
+#
+# TODO(@Ericson2314): No native exception
+[[ -z ${strictDeps-} ]] || (( "$hostOffset" < 0 )) || return 0
+
+bintoolsWrapper_addLDVars () {
+    # See ../setup-hooks/role.bash
+    local role_post
+    getHostRoleEnvHook
+
+    if [[ -d "$1/lib64" && ! -L "$1/lib64" ]]; then
+        export NIX_LDFLAGS${role_post}+=" -L$1/lib64"
+    fi
+
+    if [[ -d "$1/lib" ]]; then
+        # Don't add the /lib directory if it actually doesn't contain any libraries. For instance,
+        # Python and Haskell packages often only have directories like $out/lib/ghc-8.4.3/ or
+        # $out/lib/python3.6/, so having them in LDFLAGS just makes the linker search unnecessary
+        # directories and bloats the size of the environment variable space.
+        local -a glob=( $1/lib/lib* )
+        if [ "${#glob[*]}" -gt 0 ]; then
+            export NIX_LDFLAGS${role_post}+=" -L$1/lib"
+        fi
+    fi
+}
+
+# See ../setup-hooks/role.bash
+getTargetRole
+getTargetRoleWrapper
+
+addEnvHooks "$targetOffset" bintoolsWrapper_addLDVars
+
+# shellcheck disable=SC2157
+if [ -n "@bintools_bin@" ]; then
+    addToSearchPath _PATH @bintools_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
+
+# Export tool environment variables so various build systems use the right ones.
+
+export NIX_BINTOOLS${role_post}=@out@
+
+for cmd in \
+    ar as ld nm objcopy objdump readelf ranlib strip strings size windres
+do
+    if
+        PATH=$_PATH type -p "@targetPrefix@${cmd}" > /dev/null
+    then
+        export "${cmd^^}${role_post}=@targetPrefix@${cmd}";
+    fi
+done
+
+# If unset, assume the default hardening flags.
+: ${NIX_HARDENING_ENABLE="@default_hardening_flags_str@"}
+export NIX_HARDENING_ENABLE
+
+# No local scope in sourced file
+unset -v role_post cmd upper_case