syndicate_utils: 20240509 -> 20250110
[NixPkgs.git] / pkgs / development / compilers / gcc / common / libgcc.nix
blob3d4bd9067763200bcc7d84bd8099cf3bdf7d8e1f
2   lib,
3   stdenv,
4   version,
5   langC,
6   langCC,
7   langJit,
8   enableShared,
9   targetPlatform,
10   hostPlatform,
11   withoutTargetLibc,
12   libcCross,
15 assert !stdenv.targetPlatform.hasSharedLibraries -> !enableShared;
17 drv:
18 lib.pipe drv
20   (
21     [
23       (
24         pkg:
25         pkg.overrideAttrs (
26           previousAttrs:
27           lib.optionalAttrs
28             (targetPlatform != hostPlatform && (enableShared || targetPlatform.isMinGW) && withoutTargetLibc)
29             {
30               makeFlags = [
31                 "all-gcc"
32                 "all-target-libgcc"
33               ];
34               installTargets = "install-gcc install-target-libgcc";
35             }
36         )
37       )
39     ]
40     ++
42       # nixpkgs did not add the "libgcc" output until gcc11.  In theory
43       # the following condition can be changed to `true`, but that has not
44       # been tested.
45       lib.optionals (lib.versionAtLeast version "11.0")
47         (
48           let
49             targetPlatformSlash = if hostPlatform == targetPlatform then "" else "${targetPlatform.config}/";
51             # If we are building a cross-compiler and the target libc provided
52             # to us at build time has a libgcc, use that instead of building a
53             # new one.  This avoids having two separate (but identical) libgcc
54             # outpaths in the closure of most packages, which can be confusing.
55             useLibgccFromTargetLibc = libcCross != null && libcCross ? passthru.libgcc;
57             enableLibGccOutput =
58               (!stdenv.targetPlatform.isWindows || (with stdenv; targetPlatform == hostPlatform))
59               && !langJit
60               && !stdenv.hostPlatform.isDarwin
61               && enableShared
62               && !useLibgccFromTargetLibc;
64             # For some reason libgcc_s.so has major-version "2" on m68k but
65             # "1" everywhere else.  Might be worth changing this to "*".
66             libgcc_s-version-major = if targetPlatform.isM68k then "2" else "1";
68           in
69           [
71             (
72               pkg:
73               pkg.overrideAttrs (
74                 previousAttrs:
75                 lib.optionalAttrs useLibgccFromTargetLibc {
76                   passthru = (previousAttrs.passthru or { }) // {
77                     inherit (libcCross) libgcc;
78                   };
79                 }
80               )
81             )
83             (
84               pkg:
85               pkg.overrideAttrs (
86                 previousAttrs:
87                 lib.optionalAttrs ((!langC) || langJit || enableLibGccOutput) {
88                   outputs = previousAttrs.outputs ++ lib.optionals enableLibGccOutput [ "libgcc" ];
89                   # This is a separate phase because gcc assembles its phase scripts
90                   # in bash instead of nix (we should fix that).
91                   preFixupPhases =
92                     (previousAttrs.preFixupPhases or [ ])
93                     ++ lib.optionals ((!langC) || enableLibGccOutput) [ "preFixupLibGccPhase" ];
94                   preFixupLibGccPhase =
95                     # delete extra/unused builds of libgcc_s in non-langC builds
96                     # (i.e. libgccjit, gnat, etc) to avoid potential confusion
97                     lib.optionalString (!langC) ''
98                       rm -f $out/lib/libgcc_s.so*
99                     ''
101                     # move `libgcc_s.so` into its own output, `$libgcc`
102                     # We maintain $libgcc/lib/$target/ structure to make sure target
103                     # strip runs over libgcc_s.so and remove debug references to headers:
104                     #   https://github.com/NixOS/nixpkgs/issues/316114
105                     + lib.optionalString enableLibGccOutput (
106                       ''
107                         # move libgcc from lib to its own output (libgcc)
108                         mkdir -p $libgcc/${targetPlatformSlash}lib
109                         mv    $lib/${targetPlatformSlash}lib/libgcc_s.so      $libgcc/${targetPlatformSlash}lib/
110                         mv    $lib/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major}    $libgcc/${targetPlatformSlash}lib/
111                         ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so   $lib/${targetPlatformSlash}lib/
112                         ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major} $lib/${targetPlatformSlash}lib/
113                       ''
114                       + lib.optionalString (targetPlatformSlash != "") ''
115                         ln -s ${targetPlatformSlash}lib $libgcc/lib
116                       ''
117                       #
118                       # Nixpkgs ordinarily turns dynamic linking into pseudo-static linking:
119                       # libraries are still loaded dynamically, exactly which copy of each
120                       # library is loaded is permanently fixed at compile time (via RUNPATH).
121                       # For libgcc_s we must revert to the "impure dynamic linking" style found
122                       # in imperative software distributions.  We must do this because
123                       # `libgcc_s` calls `malloc()` and therefore has a `DT_NEEDED` for `libc`,
124                       # which creates two problems:
125                       #
126                       #  1. A circular package dependency `glibc`<-`libgcc`<-`glibc`
127                       #
128                       #  2. According to the `-Wl,-rpath` flags added by Nixpkgs' `ld-wrapper`,
129                       #     the two versions of `glibc` in the cycle above are actually
130                       #     different packages.  The later one is compiled by this `gcc`, but
131                       #     the earlier one was compiled by the compiler *that compiled* this
132                       #     `gcc` (usually the bootstrapFiles).  In any event, the `glibc`
133                       #     dynamic loader won't honor that specificity without namespaced
134                       #     manual loads (`dlmopen()`).  Once a `libc` is present in the address
135                       #     space of a process, that `libc` will be used to satisfy all
136                       #     `DT_NEEDED`s for `libc`, regardless of `RUNPATH`s.
137                       #
138                       # So we wipe the RUNPATH using `patchelf --set-rpath ""`.  We can't use
139                       # `patchelf --remove-rpath`, because at least as of patchelf 0.15.0 it
140                       # will leave the old RUNPATH string in the file where the reference
141                       # scanner can still find it:
142                       #
143                       #   https://github.com/NixOS/patchelf/issues/453
144                       #
145                       # Note: we might be using the bootstrapFiles' copy of patchelf, so we have
146                       # to keep doing it this way until both the issue is fixed *and* all the
147                       # bootstrapFiles are regenerated, on every platform.
148                       #
149                       # This patchelfing is *not* effectively equivalent to copying
150                       # `libgcc_s` into `glibc`'s outpath.  There is one minor and one
151                       # major difference:
152                       #
153                       # 1. (Minor): multiple builds of `glibc` (say, with different
154                       #    overrides or parameters) will all reference a single store
155                       #    path:
156                       #
157                       #      /nix/store/xxx...xxx-gcc-libgcc/lib/libgcc_s.so.1
158                       #
159                       #    This many-to-one referrer relationship will be visible in the store's
160                       #    dependency graph, and will be available to `nix-store -q` queries.
161                       #    Copying `libgcc_s` into each of its referrers would lose that
162                       #    information.
163                       #
164                       # 2. (Major): by referencing `libgcc_s.so.1`, rather than copying it, we
165                       #    are still able to run `nix-store -qd` on it to find out how it got
166                       #    built!  Most importantly, we can see from that deriver which compiler
167                       #    was used to build it (or if it is part of the unpacked
168                       #    bootstrap-files).  Copying `libgcc_s.so.1` from one outpath to
169                       #    another eliminates the ability to make these queries.
170                       #
171                       + ''
172                         patchelf --set-rpath "" $libgcc/lib/libgcc_s.so.${libgcc_s-version-major}
173                       ''
174                     );
175                 }
176               )
177             )
178           ]
179         )
180   )