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:

  36ad0b17a7/scripts/build/cc/gcc.sh (L638-L640)
This commit is contained in:
Adam Joseph 2023-06-16 14:15:36 -07:00
parent e41f217257
commit 2affd455a4
4 changed files with 20 additions and 8 deletions

View File

@ -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

View File

@ -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
''

View File

@ -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;
};

View File

@ -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;