glibcCross: use a libgcc built separately from gcc
### Summary This PR completely and finally solves the gcc<->glibc circular `buildInputs` problem, for cross compilation. The same technique can be applied to native builds in the future. Closes #213453 ### Motivation Prior to this PR, we had the following circular `buildInputs` problem: 1. gcc has glibc in its `buildInputs` - a compiled copy of glibc must be present before building gcc; if it isn't, gcc cripples itself (`inhibit_libc`) and refuses to build libgcc_s.so 2. glibc has libgcc_s.so in its `buildInputs` - glibc `dlopen()`s libgcc_s.so in order to implement POSIX thread cancellation. For security reasons `glibc` requires that the path to `libgcc_s.so` is [hardwired] into `glibc` at compile time, so it's technically not a true dynamic link -- it just pretends to be one. 3. libgcc_s.so is built in the same derivation as gcc - libgcc_s.so is built as part of the gcc build process We must cut one of these three links in the loop. ### Previous Attempts Previously https://github.com/NixOS/nixpkgs/pull/238154 had attempted to cut link (1) by building `gcc` without `glibc`, and using the `libgcc_s` which emerges from that build. Unfortunately this just doesn't work. GCC's configure script extracts quite a lot of information from the glibc headers (which are a build artifact -- you can't just copy them out of the source tarball) and various `./configure`-driven linking attempts. If `glibc` isn't around at build time you wind up with a `libgcc_s.so` that is missing various unwinder features (see https://github.com/NixOS/nixpkgs/issues/213453 for the most problematic one). Musl "cuts" link (2), or rather never creates it in the first place. ["Cancellation cleanup handling in musl has no relationship to C++ exceptions and unwinding... glibc implements cancellation as an exception"](https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-cancellation). IMHO Musl made the smarter decision here. It is incredibly rare to find a codebase that uses both POSIX thread cancellation *and* C++ exceptions. I have never seen a codebase that uses both *and* expects them to be aware of each other, and I would be astonished if one existed. Glibc paid an immense cost in complexity for something nobody has ever used. ### Changes Made This PR cuts link (3): instead of building libgcc_s.so as part of gcc, we build it separately from gcc. Now there is a strict acyclic graph of `buildInputs`: ``` gccWithoutTargetLibc | +--->glibc-nolibgcc | | | v +--->libgcc | | | v +--->glibc | | | v +--->gcc ``` In other words, there's a simple linear `buildInputs` chain `glibc-nolibgcc` `->` `libgcc` `->` `glibc` `->` `gcc` where all four packages are compiled by (and therefore have as a `(native)BuildInput`) `gccWithoutTargetLibc`. `gccWithoutTargetLibc` and `glibc-nolibgcc` are strictly bootstrapping artifacts; nothing else has them as a `buildInput` and they shouldn't appear in the closure of any final deployment packages. `glibc-nolibgcc` lacks `libgcc_s.so`, so it will segfault if you try to use it with POSIX thread cancellation. Fortunately all we need from it is (a) its headers (`lib.getDev`) and (b) to use it in the `./configure` script for `libgcc`. When translated over to the native bootstrap, `xgcc` takes the place of `gccWithoutTargetLibc`, and the "first `glibc`" (we build two of them) takes the place of `glibc-nolibgcc`. At that point our native and cross bootstrap have the same overall architecture, and it becomes possible to merge them (at last!) [213453]: https://github.com/NixOS/nixpkgs/issues/213453 [238154]: https://github.com/NixOS/nixpkgs/pull/238154 [hardwired]: https://github.com/NixOS/nixpkgs/blob/7553d0fe29801938bcb280bb324b579ef9016aea/pkgs/development/libraries/glibc/default.nix#L69-L88
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
, withGd ? false
|
||||
, withLibcrypt? false
|
||||
, buildPackages
|
||||
, libgcc
|
||||
}:
|
||||
|
||||
let
|
||||
@@ -16,7 +17,7 @@ in
|
||||
|
||||
(callPackage ./common.nix { inherit stdenv; } {
|
||||
inherit withLinuxHeaders withGd profilingLibraries withLibcrypt;
|
||||
pname = "glibc" + lib.optionalString withGd "-gd";
|
||||
pname = "glibc" + lib.optionalString withGd "-gd" + lib.optionalString (stdenv.cc.isGNU && libgcc==null) "-nolibgcc";
|
||||
}).overrideAttrs(previousAttrs: {
|
||||
|
||||
# Note:
|
||||
@@ -90,8 +91,8 @@ in
|
||||
#
|
||||
makeFlags =
|
||||
(previousAttrs.makeFlags or [])
|
||||
++ lib.optionals (stdenv.cc.cc?libgcc) [
|
||||
"user-defined-trusted-dirs=${stdenv.cc.cc.libgcc}/lib"
|
||||
++ lib.optionals (libgcc != null) [
|
||||
"user-defined-trusted-dirs=${libgcc}/lib"
|
||||
];
|
||||
|
||||
postInstall = previousAttrs.postInstall + (if stdenv.hostPlatform == stdenv.buildPlatform then ''
|
||||
@@ -166,8 +167,8 @@ in
|
||||
|
||||
passthru =
|
||||
(previousAttrs.passthru or {})
|
||||
// lib.optionalAttrs (stdenv.cc.cc?libgcc) {
|
||||
inherit (stdenv.cc.cc) libgcc;
|
||||
// lib.optionalAttrs (libgcc != null) {
|
||||
inherit libgcc;
|
||||
};
|
||||
|
||||
meta = (previousAttrs.meta or {}) // { description = "The GNU C Library"; };
|
||||
|
||||
@@ -21439,6 +21439,11 @@ with pkgs;
|
||||
# Being redundant to avoid cycles on boot. TODO: find a better way
|
||||
glibcCross = callPackage ../development/libraries/glibc {
|
||||
stdenv = gccCrossLibcStdenv; # doesn't compile without gcc
|
||||
libgcc = callPackage ../development/libraries/gcc/libgcc {
|
||||
gcc = gccCrossLibcStdenv.cc;
|
||||
glibc = glibcCross.override { libgcc = null; };
|
||||
stdenvNoLibs = gccCrossLibcStdenv;
|
||||
};
|
||||
};
|
||||
|
||||
muslCross = musl.override {
|
||||
|
||||
Reference in New Issue
Block a user