Skip to content

Commit

Permalink
gccCrossStageStatic: enable dynamic libraries, rename to gccWithoutTa…
Browse files Browse the repository at this point in the history
…rgetLibc

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
  • Loading branch information
Adam Joseph committed Jul 1, 2023
1 parent e41f217 commit 2affd45
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 8 deletions.
1 change: 0 additions & 1 deletion pkgs/development/compilers/gcc/common/configure-flags.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 16 additions & 2 deletions pkgs/development/compilers/gcc/common/pre-configure.nix
Original file line number Diff line number Diff line change
@@ -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
}:

Expand Down Expand Up @@ -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
''
Expand Down
2 changes: 1 addition & 1 deletion pkgs/os-specific/windows/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
7 changes: 3 additions & 4 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -15292,7 +15292,6 @@ with pkgs;
langCC = false;
libcCross = libcCross1;
targetPackages.stdenv.cc.bintools = binutilsNoLibc;
enableShared = false;
};
bintools = binutilsNoLibc;
libc = libcCross1;
Expand Down

0 comments on commit 2affd45

Please sign in to comment.