1 # The Nixpkgs CC is not directly usable, since it doesn't know where
2 # the C library and standard header files are. Therefore the compiler
3 # produced by that package cannot be installed directly in a user
4 # environment and used from the command line. So we use a wrapper
5 # script that sets up the right environment variables so that the
6 # compiler and the linker just "work".
12 , bintools ? null, libc ? null, coreutils ? null, gnugrep ? null
15 , sharedLibraryLoader ?
18 else if stdenvNoCC.targetPlatform.isNetBSD then
19 if !(targetPackages ? netbsd) then
21 else if libc != targetPackages.netbsd.headers then
22 targetPackages.netbsd.ld_elf_so
27 , nativeTools, noLibc ? false, nativeLibc, nativePrefix ? ""
28 , propagateDoc ? bintools != null && bintools ? man
29 , extraPackages ? [], extraBuildCommands ? ""
30 , isGNU ? bintools.isGNU or false
31 , isLLVM ? bintools.isLLVM or false
32 , isCCTools ? bintools.isCCTools or false
33 , expand-response-params
37 # Note: the hardening flags are part of the bintools-wrapper, rather than
38 # the cc-wrapper, because a few of them are handled by the linker.
39 , defaultHardeningFlags ? [
49 ] ++ lib.optional (with stdenvNoCC; lib.any (x: x) [
50 # OpenBSD static linking requires PIE
51 (with targetPlatform; isOpenBSD && isStatic)
53 # Musl-based platforms will keep "pie", other platforms will not.
54 # If you change this, make sure to update section `{#sec-hardening-in-nixpkgs}`
55 # in the nixpkgs manual to inform users about the defaults.
56 (targetPlatform.libc == "musl")
58 # - static aarch64, where compilation works, but produces segfaulting dynamically linked binaries.
59 # - static armv7l, where compilation fails.
60 (!(targetPlatform.isAarch && targetPlatform.isStatic))
65 assert propagateDoc -> bintools ? man;
66 assert nativeTools -> !propagateDoc && nativePrefix != "";
67 assert !nativeTools -> bintools != null && coreutils != null && gnugrep != null;
68 assert !(nativeLibc && noLibc);
69 assert (noLibc || nativeLibc) == (libc == null);
90 inherit (stdenvNoCC) hostPlatform targetPlatform;
92 # Prefix for binaries. Customarily ends with a dash separator.
94 # TODO(@Ericson2314) Make unconditional, or optional but always true by
96 targetPrefix = optionalString (targetPlatform != hostPlatform)
97 (targetPlatform.config + "-");
99 bintoolsVersion = getVersion bintools;
100 bintoolsName = removePrefix targetPrefix (getName bintools);
102 libc_bin = optionalString (libc != null) (getBin libc);
103 libc_dev = optionalString (libc != null) (getDev libc);
104 libc_lib = optionalString (libc != null) (getLib libc);
105 bintools_bin = optionalString (!nativeTools) (getBin bintools);
106 # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
107 coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
109 # See description in cc-wrapper.
110 suffixSalt = replaceStrings ["-" "."] ["_" "_"] targetPlatform.config
111 + lib.optionalString (targetPlatform.isDarwin && targetPlatform.isStatic) "_static";
113 # The dynamic linker has different names on different platforms. This is a
114 # shell glob that ought to match it.
116 /**/ if sharedLibraryLoader == null then ""
117 else if targetPlatform.libc == "musl" then "${sharedLibraryLoader}/lib/ld-musl-*"
118 else if targetPlatform.libc == "uclibc" then "${sharedLibraryLoader}/lib/ld*-uClibc.so.1"
119 else if (targetPlatform.libc == "bionic" && targetPlatform.is32bit) then "/system/bin/linker"
120 else if (targetPlatform.libc == "bionic" && targetPlatform.is64bit) then "/system/bin/linker64"
121 else if targetPlatform.libc == "nblibc" then "${sharedLibraryLoader}/libexec/ld.elf_so"
122 else if targetPlatform.system == "i686-linux" then "${sharedLibraryLoader}/lib/ld-linux.so.2"
123 else if targetPlatform.system == "x86_64-linux" then "${sharedLibraryLoader}/lib/ld-linux-x86-64.so.2"
124 else if targetPlatform.system == "s390x-linux" then "${sharedLibraryLoader}/lib/ld64.so.1"
125 # ELFv1 (.1) or ELFv2 (.2) ABI
126 else if targetPlatform.isPower64 then "${sharedLibraryLoader}/lib/ld64.so.*"
127 # ARM with a wildcard, which can be "" or "-armhf".
128 else if (with targetPlatform; isAarch32 && isLinux) then "${sharedLibraryLoader}/lib/ld-linux*.so.3"
129 else if targetPlatform.system == "aarch64-linux" then "${sharedLibraryLoader}/lib/ld-linux-aarch64.so.1"
130 else if targetPlatform.system == "powerpc-linux" then "${sharedLibraryLoader}/lib/ld.so.1"
131 else if targetPlatform.system == "s390-linux" then "${sharedLibraryLoader}/lib/ld.so.1"
132 else if targetPlatform.system == "s390x-linux" then "${sharedLibraryLoader}/lib/ld64.so.1"
133 else if targetPlatform.isMips then "${sharedLibraryLoader}/lib/ld.so.1"
134 # `ld-linux-riscv{32,64}-<abi>.so.1`
135 else if targetPlatform.isRiscV then "${sharedLibraryLoader}/lib/ld-linux-riscv*.so.1"
136 else if targetPlatform.isLoongArch64 then "${sharedLibraryLoader}/lib/ld-linux-loongarch*.so.1"
137 else if targetPlatform.isDarwin then "/usr/lib/dyld"
138 else if targetPlatform.isFreeBSD then "${sharedLibraryLoader}/libexec/ld-elf.so.1"
139 else if targetPlatform.isOpenBSD then "${sharedLibraryLoader}/libexec/ld.so"
140 else if hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
145 stdenvNoCC.mkDerivation {
147 + (if name != "" then name else "${bintoolsName}-wrapper");
148 version = optionalString (bintools != null) bintoolsVersion;
150 preferLocalBuild = true;
152 outputs = [ "out" ] ++ optionals propagateDoc ([ "man" ] ++ optional (bintools ? info) "info");
155 inherit targetPrefix suffixSalt;
156 inherit bintools libc nativeTools nativeLibc nativePrefix isGNU isLLVM;
158 emacsBufferSetup = pkgs: ''
159 ; We should handle propagation here too
162 (when (file-directory-p (concat arg "/lib"))
163 (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib")))
164 (when (file-directory-p (concat arg "/lib64"))
165 (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib64"))))
166 '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
169 inherit defaultHardeningFlags;
173 dontConfigure = true;
175 enableParallelBuilding = true;
183 mkdir -p $out/bin $out/nix-support
189 export use_response_file_by_default=${if isCCTools then "1" else "0"}
190 substituteAll "$wrapper" "$out/bin/$dst"
191 chmod +x "$out/bin/$dst"
195 + (if nativeTools then ''
196 echo ${nativePrefix} > $out/nix-support/orig-bintools
198 ldPath="${nativePrefix}/bin"
200 echo $bintools_bin > $out/nix-support/orig-bintools
202 ldPath="${bintools_bin}/bin"
205 # Solaris needs an additional ld wrapper.
206 + optionalString (targetPlatform.isSunOS && nativePrefix != "") ''
207 ldPath="${nativePrefix}/bin"
208 exec="$ldPath/${targetPrefix}ld"
209 wrap ld-solaris ${./ld-solaris-wrapper.sh}
212 # If we are asked to wrap `gas` and this bintools has it,
213 # then symlink it (`as` will be symlinked next).
214 # This is mainly for the wrapped gnat-bootstrap on x86-64 Darwin,
215 # as it must have both the GNU assembler from cctools (installed as `gas`)
216 # and the Clang integrated assembler (installed as `as`).
217 # See pkgs/os-specific/darwin/binutils/default.nix for details.
218 + optionalString wrapGas ''
219 if [ -e $ldPath/${targetPrefix}gas ]; then
220 ln -s $ldPath/${targetPrefix}gas $out/bin/${targetPrefix}gas
224 # Create symlinks for rest of the binaries.
226 for binary in objdump objcopy size strings as ar nm gprof dwp c++filt addr2line \
227 ranlib readelf elfedit dlltool dllwrap windmc windres; do
228 if [ -e $ldPath/${targetPrefix}''${binary} ]; then
229 ln -s $ldPath/${targetPrefix}''${binary} $out/bin/${targetPrefix}''${binary}
233 if [ -e ''${ld:-$ldPath/${targetPrefix}ld} ]; then
234 wrap ${targetPrefix}ld ${./ld-wrapper.sh} ''${ld:-$ldPath/${targetPrefix}ld}
237 for variant in $ldPath/${targetPrefix}ld.*; do
238 basename=$(basename "$variant")
239 wrap $basename ${./ld-wrapper.sh} $variant
244 depsTargetTargetPropagated = extraPackages;
247 ../setup-hooks/role.bash
253 ## General libc support
255 optionalString (libc != null) (''
256 touch "$out/nix-support/libc-ldflags"
257 echo "-L${libc_lib}${libc.libdir or "/lib"}" >> $out/nix-support/libc-ldflags
259 echo "${libc_lib}" > $out/nix-support/orig-libc
260 echo "${libc_dev}" > $out/nix-support/orig-libc-dev
264 ## Dynamic linker support
266 + optionalString (sharedLibraryLoader != null) ''
267 if [[ -z ''${dynamicLinker+x} ]]; then
268 echo "Don't know the name of the dynamic linker for platform '${targetPlatform.config}', so guessing instead." >&2
269 local dynamicLinker="${sharedLibraryLoader}/lib/ld*.so.?"
273 # Expand globs to fill array of options
275 dynamicLinker=($dynamicLinker)
277 case ''${#dynamicLinker[@]} in
278 0) echo "No dynamic linker found for platform '${targetPlatform.config}'." >&2;;
279 1) echo "Using dynamic linker: '$dynamicLinker'" >&2;;
280 *) echo "Multiple dynamic linkers found for platform '${targetPlatform.config}'." >&2;;
283 if [ -n "''${dynamicLinker-}" ]; then
284 echo $dynamicLinker > $out/nix-support/dynamic-linker
286 ${if targetPlatform.isDarwin then ''
287 printf "export LD_DYLD_PATH=%q\n" "$dynamicLinker" >> $out/nix-support/setup-hook
288 '' else optionalString (sharedLibraryLoader != null) ''
289 if [ -e ${sharedLibraryLoader}/lib/32/ld-linux.so.2 ]; then
290 echo ${sharedLibraryLoader}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
292 touch $out/nix-support/ld-set-dynamic-linker
301 # Propagate the underling unwrapped bintools so that if you
302 # install the wrapper, you get tools like objdump (same for any
304 + optionalString (!nativeTools) ''
305 printWords ${bintools_bin} ${optionalString (libc != null) libc_bin} > $out/nix-support/propagated-user-env-packages
309 ## Man page and info support
311 + optionalString propagateDoc (''
312 ln -s ${bintools.man} $man
313 '' + optionalString (bintools ? info) ''
314 ln -s ${bintools.info} $info
321 # some linkers on some platforms don't support specific -z flags
323 export hardening_unsupported_flags=""
324 if [[ "$($ldPath/${targetPrefix}ld -z now 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
325 hardening_unsupported_flags+=" bindnow"
327 if [[ "$($ldPath/${targetPrefix}ld -z relro 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
328 hardening_unsupported_flags+=" relro"
332 + optionalString hostPlatform.isCygwin ''
333 hardening_unsupported_flags+=" pic"
336 + optionalString (targetPlatform.isAvr || targetPlatform.isWindows) ''
337 hardening_unsupported_flags+=" relro bindnow"
340 + optionalString (libc != null && targetPlatform.isAvr) ''
341 for isa in avr5 avr3 avr4 avr6 avr25 avr31 avr35 avr51 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack; do
342 echo "-L${getLib libc}/avr/lib/$isa" >> $out/nix-support/libc-cflags
347 ## GNU specific extra strip flags
350 # TODO(@sternenseemann): make a generic strip wrapper?
351 + optionalString (bintools.isGNU or false || bintools.isLLVM or false || bintools.isCCTools or false) ''
352 wrap ${targetPrefix}strip ${./gnu-binutils-strip-wrapper.sh} \
353 "${bintools_bin}/bin/${targetPrefix}strip"
357 ### Remove certain timestamps from final binaries
359 + optionalString (targetPlatform.isDarwin && !(bintools.isGNU or false)) ''
360 echo "export ZERO_AR_DATE=1" >> $out/nix-support/setup-hook
364 for flags in "$out/nix-support"/*flags*; do
365 substituteInPlace "$flags" --replace $'\n' ' '
368 substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
369 substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
370 substituteAll ${../wrapper-common/utils.bash} $out/nix-support/utils.bash
371 substituteAll ${../wrapper-common/darwin-sdk-setup.bash} $out/nix-support/darwin-sdk-setup.bash
375 ### Ensure consistent LC_VERSION_MIN_MACOSX
377 + optionalString targetPlatform.isDarwin ''
378 substituteAll ${./add-darwin-ldflags-before.sh} $out/nix-support/add-local-ldflags-before.sh
382 ## LLVM ranlab lacks -t option that libtool expects. We can just
386 + optionalString (isLLVM && targetPlatform.isOpenBSD) ''
387 rm $out/bin/${targetPrefix}ranlib
389 ${targetPrefix}ranlib ${./llvm-ranlib-wrapper.sh} \
390 "${bintools_bin}/bin/${targetPrefix}ranlib"
394 ## Extra custom steps
396 + extraBuildCommands;
399 # for substitution in utils.bash
400 # TODO(@sternenseemann): invent something cleaner than passing in "" in case of absence
401 expandResponseParams = "${expand-response-params}/bin/expand-response-params";
402 # TODO(@sternenseemann): rename env var via stdenv rebuild
403 shell = (getBin runtimeShell + runtimeShell.shellPath or "");
404 gnugrep_bin = optionalString (!nativeTools) gnugrep;
405 rm = if nativeTools then "rm" else lib.getExe' coreutils "rm";
406 mktemp = if nativeTools then "mktemp" else lib.getExe' coreutils "mktemp";
407 wrapperName = "BINTOOLS_WRAPPER";
408 inherit dynamicLinker targetPrefix suffixSalt coreutils_bin;
409 inherit bintools_bin libc_bin libc_dev libc_lib;
410 default_hardening_flags_str = builtins.toString defaultHardeningFlags;
411 } // lib.mapAttrs (_: lib.optionalString targetPlatform.isDarwin) {
412 # These will become empty strings when not targeting Darwin.
413 inherit (targetPlatform)
414 darwinPlatform darwinSdkVersion
415 darwinMinVersion darwinMinVersionVariable;
416 } // lib.optionalAttrs (apple-sdk != null && stdenvNoCC.targetPlatform.isDarwin) {
417 # Wrapped compilers should do something useful even when no SDK is provided at `DEVELOPER_DIR`.
418 fallback_sdk = apple-sdk.__spliced.buildTarget or apple-sdk;
422 let bintools_ = optionalAttrs (bintools != null) bintools; in
423 (optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta ["priority"])) //
425 attrByPath ["meta" "description"] "System binary utilities" bintools_
426 + " (wrapper script)";