From 2affd455a40a28825f356307ce5bd8fa2f202217 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Fri, 16 Jun 2023 14:15:36 -0700 Subject: [PATCH] gccCrossStageStatic: enable dynamic libraries, rename to gccWithoutTargetLibc This commit allows `gccCrossStageStatic` to build dynamically-linked libraries. Since is no longer restricted to building static libraries its name is no longer appropriate, and this commit also renames it to the more-accurate `gccWithoutTargetLibc`. By default, you can't build a gcc that knows how to create dynamic libraries unless you have already built the targetPlatform libc. Because of this, our gcc cross-compiler is built in two stages: 1. Build a cross-compiler (gccCrossStageStatic) that can build only static libraries. 2. Use gccCrossStageStatic to compile the targetPlatform libc. 3. Use the targetPlatform libc to build a fully-capable cross compiler. You might notice that this pattern looks very similar to what we do with `xgcc` in the stdenv bootstrap. Indeed it is! I would like to work towards getting the existing stdenv bootstrap to handle cross compilers as well. However we don't want to cripple `stdenv.xgcc` by taking away its ability to build dynamic libraries. It turns out that the only thing gcc needs the targetPlatform libc for is to emit a DT_NEEDED for `-lc` into `libgcc.so`. That's it! And since we don't use `gccCrossStageStatic` to build anything other than libc, it's safe to omit the `DT_NEEDED` because that `libgcc` will never be loaded by anything other than `libc`. So `libc` will already be in the process's address space. Other people have noticed this; crosstool-ng has been using this approach for a very long time: https://github.com/crosstool-ng/crosstool-ng/blob/36ad0b17a732aaffe4701d5d8d410d6e3e3abba9/scripts/build/cc/gcc.sh#L638-L640 --- .../compilers/gcc/common/configure-flags.nix | 1 - .../compilers/gcc/common/pre-configure.nix | 18 ++++++++++++++++-- pkgs/os-specific/windows/default.nix | 2 +- pkgs/top-level/all-packages.nix | 7 +++---- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/pkgs/development/compilers/gcc/common/configure-flags.nix b/pkgs/development/compilers/gcc/common/configure-flags.nix index aa8ecc62e55a..ae4ef6bddf67 100644 --- a/pkgs/development/compilers/gcc/common/configure-flags.nix +++ b/pkgs/development/compilers/gcc/common/configure-flags.nix @@ -66,7 +66,6 @@ let "--disable-threads" "--disable-libgomp" "--disable-libquadmath" - "--disable-shared" "--disable-libatomic" # requires libc "--disable-decimal-float" # requires libc "--disable-libmpx" # requires libc diff --git a/pkgs/development/compilers/gcc/common/pre-configure.nix b/pkgs/development/compilers/gcc/common/pre-configure.nix index 58e44b96b5d3..bfdab20df1a2 100644 --- a/pkgs/development/compilers/gcc/common/pre-configure.nix +++ b/pkgs/development/compilers/gcc/common/pre-configure.nix @@ -1,10 +1,13 @@ -{ lib, version, buildPlatform, hostPlatform, targetPlatform +{ lib +, stdenv +, version, buildPlatform, hostPlatform, targetPlatform , gnat-bootstrap ? null , langAda ? false , langJava ? false , langJit ? false , langGo -, crossStageStatic +, withoutTargetLibc +, enableShared , enableMultilib }: @@ -109,6 +112,17 @@ in lib.optionalString (hostPlatform.isSunOS && hostPlatform.is64bit) '' export inhibit_libc=true '' +# Trick to build a gcc that is capable of emitting shared libraries *without* having the +# targetPlatform libc available beforehand. Taken from: +# https://web.archive.org/web/20170222224855/http://frank.harvard.edu/~coldwell/toolchain/ +# https://web.archive.org/web/20170224235700/http://frank.harvard.edu/~coldwell/toolchain/t-linux.diff ++ lib.optionalString (targetPlatform != hostPlatform && withoutTargetLibc && enableShared) + (lib.optionalString (!stdenv.targetPlatform.isPower) '' + echo 'libgcc.a: crti.o crtn.o' >> libgcc/Makefile.in + '' + '' + echo 'SHLIB_LC=' >> libgcc/Makefile.in + '') + + lib.optionalString (!enableMultilib && hostPlatform.is64bit && !hostPlatform.isMips64n32) '' export linkLib64toLib=1 '' diff --git a/pkgs/os-specific/windows/default.nix b/pkgs/os-specific/windows/default.nix index 8d6dd50548e5..12859de8a20f 100644 --- a/pkgs/os-specific/windows/default.nix +++ b/pkgs/os-specific/windows/default.nix @@ -20,7 +20,7 @@ lib.makeScope newScope (self: with self; { crossThreadsStdenv = overrideCC crossLibcStdenv (if stdenv.hostPlatform.useLLVM or false then buildPackages.llvmPackages_8.clangNoLibcxx - else buildPackages.gccCrossStageStatic.override (old: { + else buildPackages.gccWithoutTargetLibc.override (old: { bintools = old.bintools.override { libc = libcCross; }; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index faa09a9732f4..ff0b9db18e3a 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -14346,7 +14346,7 @@ with pkgs; xbursttools = callPackage ../tools/misc/xburst-tools { # It needs a cross compiler for mipsel to build the firmware it will # load into the Ben Nanonote - gccCross = pkgsCross.ben-nanonote.buildPackages.gccCrossStageStatic; + gccCross = pkgsCross.ben-nanonote.buildPackages.gccWithoutTargetLibc; autoconf = buildPackages.autoconf269; }; @@ -15266,7 +15266,7 @@ with pkgs; dontStrip = true; }))); - gccCrossLibcStdenv = overrideCC stdenv buildPackages.gccCrossStageStatic; + gccCrossLibcStdenv = overrideCC stdenv buildPackages.gccWithoutTargetLibc; crossLibcStdenv = if stdenv.hostPlatform.useLLVM or false || stdenv.hostPlatform.isDarwin @@ -15275,7 +15275,7 @@ with pkgs; # The GCC used to build libc for the target platform. Normal gccs will be # built with, and use, that cross-compiled libc. - gccCrossStageStatic = assert stdenv.targetPlatform != stdenv.hostPlatform; let + gccWithoutTargetLibc = assert stdenv.targetPlatform != stdenv.hostPlatform; let libcCross1 = binutilsNoLibc.libc; in wrapCCWith { cc = gccFun { @@ -15292,7 +15292,6 @@ with pkgs; langCC = false; libcCross = libcCross1; targetPackages.stdenv.cc.bintools = binutilsNoLibc; - enableShared = false; }; bintools = binutilsNoLibc; libc = libcCross1;