nixos/homer: init (#368594)
[NixPkgs.git] / pkgs / build-support / bintools-wrapper / default.nix
blob878439cb31a663b9d888d2effd70bb44c9daa635
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".
8 { name ? ""
9 , lib
10 , stdenvNoCC
11 , runtimeShell
12 , bintools ? null, libc ? null, coreutils ? null, gnugrep ? null
13 , apple-sdk ? null
14 , netbsd ? null
15 , sharedLibraryLoader ?
16   if libc == null then
17     null
18   else if stdenvNoCC.targetPlatform.isNetBSD then
19     if !(targetPackages ? netbsd) then
20       netbsd.ld_elf_so
21     else if libc != targetPackages.netbsd.headers then
22       targetPackages.netbsd.ld_elf_so
23     else
24       null
25   else
26     lib.getLib libc
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
34 , targetPackages ? {}
35 , wrapGas ? false
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 ? [
40     "bindnow"
41     "format"
42     "fortify"
43     "fortify3"
44     "pic"
45     "relro"
46     "stackprotector"
47     "strictoverflow"
48     "zerocallusedregs"
49   ] ++ lib.optional (with stdenvNoCC; lib.any (x: x) [
50     # OpenBSD static linking requires PIE
51     (with targetPlatform; isOpenBSD && isStatic)
52     (lib.all (x: x) [
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")
57       # Except when:
58       #    - static aarch64, where compilation works, but produces segfaulting dynamically linked binaries.
59       #    - static armv7l, where compilation fails.
60       (!(targetPlatform.isAarch && targetPlatform.isStatic))
61     ])
62   ]) "pie"
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);
71 let
72   inherit (lib)
73     attrByPath
74     concatStringsSep
75     getBin
76     getDev
77     getLib
78     getName
79     getVersion
80     hasSuffix
81     optional
82     optionalAttrs
83     optionals
84     optionalString
85     platforms
86     removePrefix
87     replaceStrings
88     ;
90   inherit (stdenvNoCC) hostPlatform targetPlatform;
92   # Prefix for binaries. Customarily ends with a dash separator.
93   #
94   # TODO(@Ericson2314) Make unconditional, or optional but always true by
95   # default.
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.
115   dynamicLinker =
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"
141     else "";
145 stdenvNoCC.mkDerivation {
146   pname = targetPrefix
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");
154   passthru = {
155     inherit targetPrefix suffixSalt;
156     inherit bintools libc nativeTools nativeLibc nativePrefix isGNU isLLVM;
158     emacsBufferSetup = pkgs: ''
159       ; We should handle propagation here too
160       (mapc
161         (lambda (arg)
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)}))
167     '';
169     inherit defaultHardeningFlags;
170   };
172   dontBuild = true;
173   dontConfigure = true;
175   enableParallelBuilding = true;
177   unpackPhase = ''
178     src=$PWD
179   '';
181   installPhase =
182     ''
183       mkdir -p $out/bin $out/nix-support
185       wrap() {
186         local dst="$1"
187         local wrapper="$2"
188         export prog="$3"
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"
192       }
193     ''
195     + (if nativeTools then ''
196       echo ${nativePrefix} > $out/nix-support/orig-bintools
198       ldPath="${nativePrefix}/bin"
199     '' else ''
200       echo $bintools_bin > $out/nix-support/orig-bintools
202       ldPath="${bintools_bin}/bin"
203     ''
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}
210     '')
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
221       fi
222     ''
224     # Create symlinks for rest of the binaries.
225     + ''
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}
230         fi
231       done
233       if [ -e ''${ld:-$ldPath/${targetPrefix}ld} ]; then
234         wrap ${targetPrefix}ld ${./ld-wrapper.sh} ''${ld:-$ldPath/${targetPrefix}ld}
235       fi
237       for variant in $ldPath/${targetPrefix}ld.*; do
238         basename=$(basename "$variant")
239         wrap $basename ${./ld-wrapper.sh} $variant
240       done
241     '';
243   strictDeps = true;
244   depsTargetTargetPropagated = extraPackages;
246   setupHooks = [
247     ../setup-hooks/role.bash
248     ./setup-hook.sh
249   ];
251   postFixup =
252     ##
253     ## General libc support
254     ##
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
261     ''
263     ##
264     ## Dynamic linker support
265     ##
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.?"
270       fi
271     ''
273     # Expand globs to fill array of options
274     + ''
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;;
281       esac
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
291           fi
292           touch $out/nix-support/ld-set-dynamic-linker
293         ''}
294       fi
295     '')
297     ##
298     ## User env support
299     ##
301     # Propagate the underling unwrapped bintools so that if you
302     # install the wrapper, you get tools like objdump (same for any
303     # binaries of libc).
304     + optionalString (!nativeTools) ''
305       printWords ${bintools_bin} ${optionalString (libc != null) libc_bin} > $out/nix-support/propagated-user-env-packages
306     ''
308     ##
309     ## Man page and info support
310     ##
311     + optionalString propagateDoc (''
312       ln -s ${bintools.man} $man
313     '' + optionalString (bintools ? info) ''
314       ln -s ${bintools.info} $info
315     '')
317     ##
318     ## Hardening support
319     ##
321     # some linkers on some platforms don't support specific -z flags
322     + ''
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"
326       fi
327       if [[ "$($ldPath/${targetPrefix}ld -z relro 2>&1 || true)" =~ un(recognized|known)\ option ]]; then
328         hardening_unsupported_flags+=" relro"
329       fi
330     ''
332     + optionalString hostPlatform.isCygwin ''
333       hardening_unsupported_flags+=" pic"
334     ''
336     + optionalString (targetPlatform.isAvr || targetPlatform.isWindows) ''
337       hardening_unsupported_flags+=" relro bindnow"
338     ''
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
343       done
344     ''
346     ##
347     ## GNU specific extra strip flags
348     ##
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"
354     ''
356     ###
357     ### Remove certain timestamps from final binaries
358     ###
359     + optionalString (targetPlatform.isDarwin && !(bintools.isGNU or false)) ''
360       echo "export ZERO_AR_DATE=1" >> $out/nix-support/setup-hook
361     ''
363     + ''
364       for flags in "$out/nix-support"/*flags*; do
365         substituteInPlace "$flags" --replace $'\n' ' '
366       done
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
372     ''
374     ###
375     ### Ensure consistent LC_VERSION_MIN_MACOSX
376     ###
377     + optionalString targetPlatform.isDarwin ''
378       substituteAll ${./add-darwin-ldflags-before.sh} $out/nix-support/add-local-ldflags-before.sh
379     ''
381     ##
382     ## LLVM ranlab lacks -t option that libtool expects. We can just
383     ## skip it
384     ##
386     + optionalString (isLLVM && targetPlatform.isOpenBSD) ''
387       rm $out/bin/${targetPrefix}ranlib
388       wrap \
389         ${targetPrefix}ranlib ${./llvm-ranlib-wrapper.sh} \
390         "${bintools_bin}/bin/${targetPrefix}ranlib"
391     ''
393     ##
394     ## Extra custom steps
395     ##
396     + extraBuildCommands;
398   env = {
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;
419   };
421   meta =
422     let bintools_ = optionalAttrs (bintools != null) bintools; in
423     (optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta ["priority"])) //
424     { description =
425         attrByPath ["meta" "description"] "System binary utilities" bintools_
426         + " (wrapper script)";
427       priority = 10;
428   };