lol
at master 510 lines 16 kB view raw
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}