lol
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".
7
8{
9 name ? "",
10 lib,
11 stdenvNoCC,
12 runtimeShell,
13 bintools ? null,
14 libc ? null,
15 coreutils ? null,
16 gnugrep ? null,
17 apple-sdk ? null,
18 netbsd ? null,
19 sharedLibraryLoader ?
20 if libc == null then
21 null
22 else if stdenvNoCC.targetPlatform.isNetBSD then
23 if !(targetPackages ? netbsd) then
24 netbsd.ld_elf_so
25 else if libc != targetPackages.netbsd.headers then
26 targetPackages.netbsd.ld_elf_so
27 else
28 null
29 else
30 lib.getLib libc,
31 nativeTools,
32 noLibc ? false,
33 nativeLibc,
34 nativePrefix ? "",
35 propagateDoc ? bintools != null && bintools ? man,
36 extraPackages ? [ ],
37 extraBuildCommands ? "",
38 isGNU ? bintools.isGNU or false,
39 isLLVM ? bintools.isLLVM or false,
40 isCCTools ? bintools.isCCTools or false,
41 expand-response-params,
42 targetPackages ? { },
43 wrapGas ? false,
44
45 # Note: the hardening flags are part of the bintools-wrapper, rather than
46 # the cc-wrapper, because a few of them are handled by the linker.
47 defaultHardeningFlags ? [
48 "bindnow"
49 "format"
50 "fortify"
51 "fortify3"
52 "pic"
53 "relro"
54 "stackclashprotection"
55 "stackprotector"
56 "strictoverflow"
57 "zerocallusedregs"
58 ]
59 ++ lib.optional (
60 with stdenvNoCC;
61 lib.any (x: x) [
62 # OpenBSD static linking requires PIE
63 (with targetPlatform; isOpenBSD && isStatic)
64 (lib.all (x: x) [
65 # Musl-based platforms will keep "pie", other platforms will not.
66 # If you change this, make sure to update section `{#sec-hardening-in-nixpkgs}`
67 # in the nixpkgs manual to inform users about the defaults.
68 (targetPlatform.libc == "musl")
69 # Except when:
70 # - static aarch64, where compilation works, but produces segfaulting dynamically linked binaries.
71 # - static armv7l, where compilation fails.
72 (!(targetPlatform.isAarch && targetPlatform.isStatic))
73 ])
74 ]
75 ) "pie",
76}:
77
78assert propagateDoc -> bintools ? man;
79assert nativeTools -> !propagateDoc && nativePrefix != "";
80assert !nativeTools -> bintools != null && coreutils != null && gnugrep != null;
81assert !(nativeLibc && noLibc);
82assert (noLibc || nativeLibc) == (libc == null);
83
84let
85 inherit (lib)
86 attrByPath
87 concatStringsSep
88 getBin
89 getDev
90 getLib
91 getName
92 getVersion
93 hasSuffix
94 optional
95 optionalAttrs
96 optionals
97 optionalString
98 platforms
99 removePrefix
100 replaceStrings
101 ;
102
103 inherit (stdenvNoCC) hostPlatform targetPlatform;
104
105 # Prefix for binaries. Customarily ends with a dash separator.
106 #
107 # TODO(@Ericson2314) Make unconditional, or optional but always true by
108 # default.
109 targetPrefix = optionalString (targetPlatform != hostPlatform) (targetPlatform.config + "-");
110
111 bintoolsVersion = getVersion bintools;
112 bintoolsName = removePrefix targetPrefix (getName bintools);
113
114 libc_bin = optionalString (libc != null) (getBin libc);
115 libc_dev = optionalString (libc != null) (getDev libc);
116 libc_lib = optionalString (libc != null) (getLib libc);
117 bintools_bin = optionalString (!nativeTools) (getBin bintools);
118 # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
119 coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
120
121 # See description in cc-wrapper.
122 suffixSalt =
123 replaceStrings [ "-" "." ] [ "_" "_" ] targetPlatform.config
124 + lib.optionalString (targetPlatform.isDarwin && targetPlatform.isStatic) "_static";
125
126 # The dynamic linker has different names on different platforms. This is a
127 # shell glob that ought to match it.
128 dynamicLinker =
129 if sharedLibraryLoader == null then
130 ""
131 else if targetPlatform.libc == "musl" then
132 "${sharedLibraryLoader}/lib/ld-musl-*"
133 else if targetPlatform.libc == "uclibc" then
134 "${sharedLibraryLoader}/lib/ld*-uClibc.so.1"
135 else if (targetPlatform.libc == "bionic" && targetPlatform.is32bit) then
136 "/system/bin/linker"
137 else if (targetPlatform.libc == "bionic" && targetPlatform.is64bit) then
138 "/system/bin/linker64"
139 else if targetPlatform.libc == "nblibc" then
140 "${sharedLibraryLoader}/libexec/ld.elf_so"
141 else if targetPlatform.system == "i686-linux" then
142 "${sharedLibraryLoader}/lib/ld-linux.so.2"
143 else if targetPlatform.system == "x86_64-linux" then
144 "${sharedLibraryLoader}/lib/ld-linux-x86-64.so.2"
145 else if targetPlatform.system == "s390x-linux" then
146 "${sharedLibraryLoader}/lib/ld64.so.1"
147 # ELFv1 (.1) or ELFv2 (.2) ABI
148 else if targetPlatform.isPower64 then
149 "${sharedLibraryLoader}/lib/ld64.so.*"
150 # ARM with a wildcard, which can be "" or "-armhf".
151 else if (with targetPlatform; isAarch32 && isLinux) then
152 "${sharedLibraryLoader}/lib/ld-linux*.so.3"
153 else if targetPlatform.system == "aarch64-linux" then
154 "${sharedLibraryLoader}/lib/ld-linux-aarch64.so.1"
155 else if targetPlatform.system == "powerpc-linux" then
156 "${sharedLibraryLoader}/lib/ld.so.1"
157 else if targetPlatform.system == "s390-linux" then
158 "${sharedLibraryLoader}/lib/ld.so.1"
159 else if targetPlatform.system == "s390x-linux" then
160 "${sharedLibraryLoader}/lib/ld64.so.1"
161 else if targetPlatform.isMips then
162 "${sharedLibraryLoader}/lib/ld.so.1"
163 # `ld-linux-riscv{32,64}-<abi>.so.1`
164 else if targetPlatform.isRiscV then
165 "${sharedLibraryLoader}/lib/ld-linux-riscv*.so.1"
166 else if targetPlatform.isLoongArch64 then
167 "${sharedLibraryLoader}/lib/ld-linux-loongarch*.so.1"
168 else if targetPlatform.isDarwin then
169 "/usr/lib/dyld"
170 else if targetPlatform.isFreeBSD then
171 "${sharedLibraryLoader}/libexec/ld-elf.so.1"
172 else if targetPlatform.isOpenBSD then
173 "${sharedLibraryLoader}/libexec/ld.so"
174 else if hasSuffix "pc-gnu" targetPlatform.config then
175 "ld.so.1"
176 else
177 "";
178
179in
180
181stdenvNoCC.mkDerivation {
182 pname = targetPrefix + (if name != "" then name else "${bintoolsName}-wrapper");
183 version = optionalString (bintools != null) bintoolsVersion;
184
185 preferLocalBuild = true;
186
187 outputs = [ "out" ] ++ optionals propagateDoc ([ "man" ] ++ optional (bintools ? info) "info");
188
189 passthru = {
190 inherit targetPrefix suffixSalt;
191 inherit
192 bintools
193 libc
194 nativeTools
195 nativeLibc
196 nativePrefix
197 isGNU
198 isLLVM
199 ;
200
201 emacsBufferSetup = pkgs: ''
202 ; We should handle propagation here too
203 (mapc
204 (lambda (arg)
205 (when (file-directory-p (concat arg "/lib"))
206 (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib")))
207 (when (file-directory-p (concat arg "/lib64"))
208 (setenv "NIX_LDFLAGS_${suffixSalt}" (concat (getenv "NIX_LDFLAGS_${suffixSalt}") " -L" arg "/lib64"))))
209 '(${concatStringsSep " " (map (pkg: "\"${pkg}\"") pkgs)}))
210 '';
211
212 inherit defaultHardeningFlags;
213 };
214
215 dontBuild = true;
216 dontConfigure = true;
217
218 enableParallelBuilding = true;
219
220 unpackPhase = ''
221 src=$PWD
222 '';
223
224 installPhase = ''
225 mkdir -p $out/bin $out/nix-support
226
227 wrap() {
228 local dst="$1"
229 local wrapper="$2"
230 export prog="$3"
231 export use_response_file_by_default=${if isCCTools then "1" else "0"}
232 substituteAll "$wrapper" "$out/bin/$dst"
233 chmod +x "$out/bin/$dst"
234 }
235 ''
236
237 + (
238 if nativeTools then
239 ''
240 echo ${nativePrefix} > $out/nix-support/orig-bintools
241
242 ldPath="${nativePrefix}/bin"
243 ''
244 else
245 ''
246 echo $bintools_bin > $out/nix-support/orig-bintools
247
248 ldPath="${bintools_bin}/bin"
249 ''
250
251 # Solaris needs an additional ld wrapper.
252 + optionalString (targetPlatform.isSunOS && nativePrefix != "") ''
253 ldPath="${nativePrefix}/bin"
254 exec="$ldPath/${targetPrefix}ld"
255 wrap ld-solaris ${./ld-solaris-wrapper.sh}
256 ''
257 )
258
259 # If we are asked to wrap `gas` and this bintools has it,
260 # then symlink it (`as` will be symlinked next).
261 # This is mainly for the wrapped gnat-bootstrap on x86-64 Darwin,
262 # as it must have both the GNU assembler from cctools (installed as `gas`)
263 # and the Clang integrated assembler (installed as `as`).
264 # See pkgs/os-specific/darwin/binutils/default.nix for details.
265 + optionalString wrapGas ''
266 if [ -e $ldPath/${targetPrefix}gas ]; then
267 ln -s $ldPath/${targetPrefix}gas $out/bin/${targetPrefix}gas
268 fi
269 ''
270
271 # Create symlinks for rest of the binaries.
272 + ''
273 for binary in objdump objcopy size strings as ar nm gprof dwp c++filt addr2line \
274 ranlib readelf elfedit dlltool dllwrap windmc windres; do
275 if [ -e $ldPath/${targetPrefix}''${binary} ]; then
276 ln -s $ldPath/${targetPrefix}''${binary} $out/bin/${targetPrefix}''${binary}
277 fi
278 done
279
280 if [ -e ''${ld:-$ldPath/${targetPrefix}ld} ]; then
281 wrap ${targetPrefix}ld ${./ld-wrapper.sh} ''${ld:-$ldPath/${targetPrefix}ld}
282 fi
283
284 for variant in $ldPath/${targetPrefix}ld.*; do
285 basename=$(basename "$variant")
286 wrap $basename ${./ld-wrapper.sh} $variant
287 done
288 '';
289
290 strictDeps = true;
291 depsTargetTargetPropagated = extraPackages;
292
293 setupHooks = [
294 ../setup-hooks/role.bash
295 ./setup-hook.sh
296 ];
297
298 postFixup =
299 ##
300 ## General libc support
301 ##
302 optionalString (libc != null) (
303 ''
304 touch "$out/nix-support/libc-ldflags"
305 echo "-L${libc_lib}${libc.libdir or "/lib"}" >> $out/nix-support/libc-ldflags
306
307 echo "${libc_lib}" > $out/nix-support/orig-libc
308 echo "${libc_dev}" > $out/nix-support/orig-libc-dev
309 ''
310
311 ##
312 ## Dynamic linker support
313 ##
314 + optionalString (sharedLibraryLoader != null) ''
315 if [[ -z ''${dynamicLinker+x} ]]; then
316 echo "Don't know the name of the dynamic linker for platform '${targetPlatform.config}', so guessing instead." >&2
317 local dynamicLinker="${sharedLibraryLoader}/lib/ld*.so.?"
318 fi
319 ''
320
321 # Expand globs to fill array of options
322 + ''
323 dynamicLinker=($dynamicLinker)
324
325 case ''${#dynamicLinker[@]} in
326 0) echo "No dynamic linker found for platform '${targetPlatform.config}'." >&2;;
327 1) echo "Using dynamic linker: '$dynamicLinker'" >&2;;
328 *) echo "Multiple dynamic linkers found for platform '${targetPlatform.config}'." >&2;;
329 esac
330
331 if [ -n "''${dynamicLinker-}" ]; then
332 echo $dynamicLinker > $out/nix-support/dynamic-linker
333
334 ${
335 if targetPlatform.isDarwin then
336 ''
337 printf "export LD_DYLD_PATH=%q\n" "$dynamicLinker" >> $out/nix-support/setup-hook
338 ''
339 else
340 optionalString (sharedLibraryLoader != null) ''
341 if [ -e ${sharedLibraryLoader}/lib/32/ld-linux.so.2 ]; then
342 echo ${sharedLibraryLoader}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
343 fi
344 touch $out/nix-support/ld-set-dynamic-linker
345 ''
346 }
347 fi
348 ''
349 + optionalString (libc.w32api or null != null) ''
350 echo '-L${lib.getLib libc.w32api}${libc.libdir or "/lib/w32api"}' >> $out/nix-support/libc-ldflags
351 ''
352 )
353
354 ##
355 ## User env support
356 ##
357
358 # Propagate the underling unwrapped bintools so that if you
359 # install the wrapper, you get tools like objdump (same for any
360 # binaries of libc).
361 + optionalString (!nativeTools) ''
362 printWords ${bintools_bin} ${
363 optionalString (libc != null) libc_bin
364 } > $out/nix-support/propagated-user-env-packages
365 ''
366
367 ##
368 ## Man page and info support
369 ##
370 + optionalString propagateDoc (
371 ''
372 ln -s ${bintools.man} $man
373 ''
374 + optionalString (bintools ? info) ''
375 ln -s ${bintools.info} $info
376 ''
377 )
378
379 ##
380 ## Hardening support
381 ##
382
383 # some linkers on some platforms don't support specific -z flags
384 + ''
385 export hardening_unsupported_flags=""
386 if [[ "$($ldPath/${targetPrefix}ld -z now 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
387 hardening_unsupported_flags+=" bindnow"
388 fi
389 if [[ "$($ldPath/${targetPrefix}ld -z relro 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
390 hardening_unsupported_flags+=" relro"
391 fi
392 ''
393
394 + optionalString hostPlatform.isCygwin ''
395 hardening_unsupported_flags+=" pic"
396 ''
397
398 + optionalString (targetPlatform.isAvr || targetPlatform.isWindows) ''
399 hardening_unsupported_flags+=" relro bindnow"
400 ''
401
402 + optionalString (libc != null && targetPlatform.isAvr) ''
403 for isa in avr5 avr3 avr4 avr6 avr25 avr31 avr35 avr51 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack; do
404 echo "-L${getLib libc}/avr/lib/$isa" >> $out/nix-support/libc-cflags
405 done
406 ''
407
408 ##
409 ## GNU specific extra strip flags
410 ##
411
412 # TODO(@sternenseemann): make a generic strip wrapper?
413 +
414 optionalString (bintools.isGNU or false || bintools.isLLVM or false || bintools.isCCTools or false)
415 ''
416 wrap ${targetPrefix}strip ${./gnu-binutils-strip-wrapper.sh} \
417 "${bintools_bin}/bin/${targetPrefix}strip"
418 ''
419
420 ###
421 ### Remove certain timestamps from final binaries
422 ###
423 + optionalString (targetPlatform.isDarwin && !(bintools.isGNU or false)) ''
424 echo "export ZERO_AR_DATE=1" >> $out/nix-support/setup-hook
425 ''
426
427 + ''
428 for flags in "$out/nix-support"/*flags*; do
429 substituteInPlace "$flags" --replace $'\n' ' '
430 done
431
432 substituteAll ${./add-flags.sh} $out/nix-support/add-flags.sh
433 substituteAll ${./add-hardening.sh} $out/nix-support/add-hardening.sh
434 substituteAll ${../wrapper-common/utils.bash} $out/nix-support/utils.bash
435 substituteAll ${../wrapper-common/darwin-sdk-setup.bash} $out/nix-support/darwin-sdk-setup.bash
436 ''
437
438 ###
439 ### Ensure consistent LC_VERSION_MIN_MACOSX
440 ###
441 + optionalString targetPlatform.isDarwin ''
442 substituteAll ${./add-darwin-ldflags-before.sh} $out/nix-support/add-local-ldflags-before.sh
443 ''
444
445 ##
446 ## LLVM ranlab lacks -t option that libtool expects. We can just
447 ## skip it
448 ##
449
450 + optionalString (isLLVM && targetPlatform.isOpenBSD) ''
451 rm $out/bin/${targetPrefix}ranlib
452 wrap \
453 ${targetPrefix}ranlib ${./llvm-ranlib-wrapper.sh} \
454 "${bintools_bin}/bin/${targetPrefix}ranlib"
455 ''
456
457 ##
458 ## Extra custom steps
459 ##
460 + extraBuildCommands;
461
462 env = {
463 # for substitution in utils.bash
464 # TODO(@sternenseemann): invent something cleaner than passing in "" in case of absence
465 expandResponseParams = "${expand-response-params}/bin/expand-response-params";
466 # TODO(@sternenseemann): rename env var via stdenv rebuild
467 shell = (getBin runtimeShell + runtimeShell.shellPath or "");
468 gnugrep_bin = optionalString (!nativeTools) gnugrep;
469 rm = if nativeTools then "rm" else lib.getExe' coreutils "rm";
470 mktemp = if nativeTools then "mktemp" else lib.getExe' coreutils "mktemp";
471 wrapperName = "BINTOOLS_WRAPPER";
472 inherit
473 dynamicLinker
474 targetPrefix
475 suffixSalt
476 coreutils_bin
477 ;
478 inherit
479 bintools_bin
480 libc_bin
481 libc_dev
482 libc_lib
483 ;
484 default_hardening_flags_str = builtins.toString defaultHardeningFlags;
485 }
486 // lib.mapAttrs (_: lib.optionalString targetPlatform.isDarwin) {
487 # These will become empty strings when not targeting Darwin.
488 inherit (targetPlatform)
489 darwinPlatform
490 darwinSdkVersion
491 darwinMinVersion
492 darwinMinVersionVariable
493 ;
494 }
495 // lib.optionalAttrs (stdenvNoCC.targetPlatform.isDarwin && apple-sdk != null) {
496 # Wrapped compilers should do something useful even when no SDK is provided at `DEVELOPER_DIR`.
497 fallback_sdk = apple-sdk.__spliced.buildTarget or apple-sdk;
498 };
499
500 meta =
501 let
502 bintools_ = optionalAttrs (bintools != null) bintools;
503 in
504 (optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta [ "priority" ]))
505 // {
506 description =
507 attrByPath [ "meta" "description" ] "System binary utilities" bintools_ + " (wrapper script)";
508 priority = 10;
509 };
510}