nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1let
2 withGold = platform: platform.isElf && !platform.isRiscV && !platform.isLoongArch64;
3in
4
5{
6 stdenv,
7 autoconf269,
8 automake,
9 libtool,
10 bison,
11 buildPackages,
12 fetchurl,
13 gettext,
14 lib,
15 noSysDirs,
16 perl,
17 runCommand,
18 zlib,
19
20 enableGold ? withGold stdenv.targetPlatform,
21 enableGoldDefault ? false,
22 # shared lib linking fails on cygwin due to multiple definitions
23 # https://cygwin.com/cgit/cygwin-packages/binutils/blame/binutils.cygport
24 enableShared ? (!stdenv.hostPlatform.isStatic && !stdenv.hostPlatform.isCygwin),
25 # WARN: Enabling all targets increases output size to a multiple.
26 withAllTargets ? false,
27}:
28
29# WARN: configure silently disables ld.gold if it's unsupported, so we need to
30# make sure that intent matches result ourselves.
31assert enableGold -> withGold stdenv.targetPlatform;
32assert enableGoldDefault -> enableGold;
33
34let
35 inherit (stdenv) buildPlatform hostPlatform targetPlatform;
36
37 version = "2.44";
38
39 #INFO: The targetPrefix prepended to binary names to allow multiple binuntils
40 # on the PATH to both be usable.
41 targetPrefix = lib.optionalString (targetPlatform != hostPlatform) "${targetPlatform.config}-";
42
43 # gas is disabled for some targets via noconfigdirs in configure.
44 targetHasGas = !stdenv.targetPlatform.isDarwin;
45
46 # gas isn't multi-target, even with --enable-targets=all, so we do
47 # separate builds of just gas for each target.
48 #
49 # There's no way to do this exhaustively, so feel free to add
50 # additional targets here as required.
51 allGasTargets =
52 allGasTargets'
53 ++ lib.optional (
54 targetHasGas && !lib.elem targetPlatform.config allGasTargets'
55 ) targetPlatform.config;
56 allGasTargets' = [
57 "aarch64-unknown-linux-gnu"
58 "alpha-unknown-linux-gnu"
59 "arm-unknown-linux-gnu"
60 "avr-unknown-linux-gnu"
61 "cris-unknown-linux-gnu"
62 "hppa-unknown-linux-gnu"
63 "i686-unknown-linux-gnu"
64 "ia64-unknown-linux-gnu"
65 "m68k-unknown-linux-gnu"
66 "mips-unknown-linux-gnu"
67 "mips64-unknown-linux-gnu"
68 "msp430-unknown-linux-gnu"
69 "powerpc-unknown-linux-gnu"
70 "powerpc64-unknown-linux-gnu"
71 "s390-unknown-linux-gnu"
72 "sparc-unknown-linux-gnu"
73 "vax-unknown-linux-gnu"
74 "x86_64-unknown-linux-gnu"
75 "xscale-unknown-linux-gnu"
76 ];
77in
78
79stdenv.mkDerivation (finalAttrs: {
80 pname = targetPrefix + "binutils";
81 inherit version;
82
83 src = fetchurl {
84 url = "mirror://gnu/binutils/binutils-with-gold-${version}.tar.bz2";
85 hash = "sha256-NHM+pJXMDlDnDbTliQ3sKKxB8OFMShZeac8n+5moxMg=";
86 };
87
88 # WARN: this package is used for bootstrapping fetchurl, and thus cannot use
89 # fetchpatch! All mutable patches (generated by GitHub or cgit) that are
90 # needed here should be included directly in Nixpkgs as files.
91 patches = [
92 # Make binutils output deterministic by default.
93 ./deterministic.patch
94
95 # Breaks nm BSD flag detection, heeds an upstream fix:
96 # https://sourceware.org/PR29547
97 ./0001-Revert-libtool.m4-fix-the-NM-nm-over-here-B-option-w.patch
98 ./0001-Revert-libtool.m4-fix-nm-BSD-flag-detection.patch
99
100 # For some reason bfd ld doesn't search DT_RPATH when cross-compiling. It's
101 # not clear why this behavior was decided upon but it has the unfortunate
102 # consequence that the linker will fail to find transitive dependencies of
103 # shared objects when cross-compiling. Consequently, we are forced to
104 # override this behavior, forcing ld to search DT_RPATH even when
105 # cross-compiling.
106 ./always-search-rpath.patch
107
108 # Avoid `lib -> out -> lib` reference. Normally `bfd-plugins` does
109 # not need to know binutils' BINDIR at all. It's an absolute path
110 # where libraries are stored.
111 ./plugins-no-BINDIR.patch
112
113 # ld64 needs `-undefined dynamic_lookup` to link `libctf-nobfd.dylib`, but the Darwin
114 # version detection in `libtool.m4` fails to detect the Darwin version correctly.
115 ./0001-libtool.m4-update-macos-version-detection-block.patch
116
117 # Adds AVR-specific options to "size" for compatibility with Atmel's downstream distribution
118 # Patch from arch-community
119 # https://github.com/archlinux/svntogit-community/blob/c8d53dd1734df7ab15931f7fad0c9acb8386904c/trunk/avr-size.patch
120 ./avr-size.patch
121
122 ./windres-locate-gcc.patch
123
124 # Backported against CVE patched in the 2.45 series. See:
125 # https://nvd.nist.gov/vuln/detail/CVE-2025-5244
126 ./CVE-2025-5244.diff
127
128 # Backported against CVE patched in the 2.45 series. See:
129 # https://nvd.nist.gov/vuln/detail/CVE-2025-5245
130 ./CVE-2025-5245.diff
131 ];
132
133 outputs = [
134 "out"
135 "info"
136 "man"
137 "dev"
138 ]
139 # Ideally we would like to always install 'lib' into a separate
140 # target. Unfortunately cross-compiled binutils installs libraries
141 # across both `$lib/lib/` and `$out/$target/lib` with a reference
142 # from $out to $lib. Probably a binutils bug: all libraries should go
143 # to $lib as binutils does not build target libraries. Let's make our
144 # life slightly simpler by installing everything into $out for
145 # cross-binutils.
146 ++ lib.optionals (targetPlatform == hostPlatform) [ "lib" ];
147
148 strictDeps = true;
149 depsBuildBuild = [ buildPackages.stdenv.cc ];
150 # texinfo was removed here in https://github.com/NixOS/nixpkgs/pull/210132
151 # to reduce rebuilds during stdenv bootstrap. Please don't add it back without
152 # checking the impact there first.
153 nativeBuildInputs = [
154 bison
155 perl
156 ]
157 ++ lib.optionals buildPlatform.isDarwin [
158 autoconf269
159 automake
160 gettext
161 libtool
162 ];
163
164 buildInputs = [
165 zlib
166 gettext
167 ];
168
169 inherit noSysDirs;
170
171 preConfigure =
172 (lib.optionalString buildPlatform.isDarwin ''
173 for i in */configure.ac; do
174 pushd "$(dirname "$i")"
175 echo "Running autoreconf in $PWD"
176 # autoreconf doesn't work, don't know why
177 # autoreconf ''${autoreconfFlags:---install --force --verbose}
178 autoconf
179 popd
180 done
181 '')
182 + ''
183 # Clear the default library search path.
184 if test "$noSysDirs" = "1"; then
185 echo 'NATIVE_LIB_DIRS=' >> ld/configure.tgt
186 fi
187
188 # Use symlinks instead of hard links to save space ("strip" in the
189 # fixup phase strips each hard link separately).
190 for i in binutils/Makefile.in gas/Makefile.in ld/Makefile.in gold/Makefile.in; do
191 sed -i "$i" -e 's|ln |ln -s |'
192 done
193
194 configureScript="$PWD/configure"
195 mkdir $NIX_BUILD_TOP/build
196 cd $NIX_BUILD_TOP/build
197 '';
198
199 # As binutils takes part in the stdenv building, we don't want references
200 # to the bootstrap-tools libgcc (as uses to happen on arm/mips)
201 #
202 # for FreeBSD it's more complicated. With -static-libgcc, configure
203 # thinks that limits.h does not exist and the build fails for not finding
204 # LONG_MIN. The configure test itself succeeds but the compiler issues a
205 # warning about -static-libgcc being unused.
206 env.NIX_CFLAGS_COMPILE =
207 if (hostPlatform.isDarwin || hostPlatform.isFreeBSD) then
208 "-Wno-string-plus-int -Wno-deprecated-declarations"
209 else
210 "-static-libgcc";
211
212 hardeningDisable = [
213 "format"
214 ];
215
216 configurePlatforms = [
217 "build"
218 "host"
219 "target"
220 ];
221
222 configureFlags = [
223 "--enable-64-bit-bfd"
224 "--with-system-zlib"
225
226 "--enable-deterministic-archives"
227 "--disable-werror"
228 "--enable-fix-loongson2f-nop"
229
230 # Turn on --enable-new-dtags by default to make the linker set
231 # RUNPATH instead of RPATH on binaries. This is important because
232 # RUNPATH can be overridden using LD_LIBRARY_PATH at runtime.
233 "--enable-new-dtags"
234
235 # force target prefix. Some versions of binutils will make it empty if
236 # `--host` and `--target` are too close, even if Nixpkgs thinks the
237 # platforms are different (e.g. because not all the info makes the
238 # `config`). Other versions of binutils will always prefix if `--target` is
239 # passed, even if `--host` and `--target` are the same. The easiest thing
240 # for us to do is not leave it to chance, and force the program prefix to be
241 # what we want it to be.
242 "--program-prefix=${targetPrefix}"
243
244 # Unconditionally disable:
245 # - musl target needs porting: https://sourceware.org/PR29477
246 "--disable-gprofng"
247
248 # By default binutils searches $libdir for libraries. This brings in
249 # libbfd and libopcodes into a default visibility. Drop default lib
250 # path to force users to declare their use of these libraries.
251 "--with-lib-path=:"
252 ]
253 ++ lib.optionals withAllTargets [
254 "--enable-targets=all"
255 # gas will be built separately for each target.
256 "--disable-gas"
257 ]
258 ++ lib.optionals enableGold [
259 "--enable-gold${lib.optionalString enableGoldDefault "=default"}"
260 "--enable-plugins"
261 ]
262 ++ (
263 if enableShared then
264 [
265 "--enable-shared"
266 "--disable-static"
267 ]
268 else
269 [
270 "--disable-shared"
271 "--enable-static"
272 ]
273 )
274 ++ (lib.optionals (stdenv.cc.bintools.isLLVM && lib.versionAtLeast stdenv.cc.bintools.version "17")
275 [
276 # lld17+ passes `--no-undefined-version` by default and makes this a hard
277 # error; libctf.ver version script references symbols that aren't present.
278 #
279 # This is fixed upstream and can be removed with the future release of 2.43.
280 # For now we allow this with `--undefined-version`:
281 "LDFLAGS=-Wl,--undefined-version"
282 ]
283 );
284
285 postConfigure = lib.optionalString withAllTargets ''
286 for target in ${lib.escapeShellArgs allGasTargets}; do
287 mkdir "$NIX_BUILD_TOP/build-$target"
288 env -C "$NIX_BUILD_TOP/build-$target" \
289 "$configureScript" $configureFlags "''${configureFlagsArray[@]}" \
290 --enable-gas --program-prefix "$target-" --target "$target"
291 done
292 '';
293
294 makeFlags = [
295 # As we regenerated configure build system tries hard to use
296 # texinfo to regenerate manuals. Let's avoid the dependency
297 # on texinfo in bootstrap path and keep manuals unmodified.
298 "MAKEINFO=true"
299 ];
300
301 postBuild = lib.optionalString withAllTargets ''
302 for target in ${lib.escapeShellArgs allGasTargets}; do
303 make -C "$NIX_BUILD_TOP/build-$target" -j"$NIX_BUILD_CORES" \
304 $makeFlags "''${makeFlagsArray[@]}" $buildFlags "''${buildFlagsArray[@]}" \
305 TARGET-gas=as-new all-gas
306 done
307 '';
308
309 # Fails
310 doCheck = false;
311
312 # Break dependency on pkgsBuildBuild.gcc when building a cross-binutils
313 stripDebugList =
314 if stdenv.hostPlatform != stdenv.targetPlatform then
315 "bin lib ${stdenv.hostPlatform.config}"
316 else
317 null;
318
319 # INFO: Otherwise it fails with:
320 # `./sanity.sh: line 36: $out/bin/size: not found`
321 doInstallCheck = (buildPlatform == hostPlatform) && (hostPlatform == targetPlatform);
322
323 enableParallelBuilding = true;
324
325 # For the same reason we don't split "lib" output we undo the $target/
326 # prefix for installed headers and libraries we link:
327 # $out/$host/$target/lib/* to $out/lib/
328 # $out/$host/$target/include/* to $dev/include/*
329 # TODO(trofi): fix installation paths upstream so we could remove this
330 # code and have "lib" output unconditionally.
331 postInstall =
332 lib.optionalString (hostPlatform.config != targetPlatform.config) ''
333 ln -s $out/${hostPlatform.config}/${targetPlatform.config}/lib/* $out/lib/
334 ln -s $out/${hostPlatform.config}/${targetPlatform.config}/include/* $dev/include/
335 ''
336 + lib.optionalString withAllTargets ''
337 for target in ${lib.escapeShellArgs allGasTargets}; do
338 make -C "$NIX_BUILD_TOP/build-$target/gas" -j"$NIX_BUILD_CORES" \
339 $makeFlags "''${makeFlagsArray[@]}" $installFlags "''${installFlagsArray[@]}" \
340 install-exec-bindir
341 done
342 ''
343 + lib.optionalString (withAllTargets && targetHasGas) ''
344 ln -s $out/bin/${stdenv.targetPlatform.config}-as $out/bin/as
345 '';
346
347 passthru = {
348 inherit targetPrefix;
349 hasGold = enableGold;
350 isGNU = true;
351
352 # The plugin API is not a function of any targets. Expose it separately,
353 # currently only used by LLVM for enabling BFD to do LTO with LLVM bitcode.
354 # (Tar will exit with an error if there are no matches).
355 plugin-api-header = runCommand "libbfd-plugin-api-header" { } ''
356 mkdir -p $out
357 tar --directory=$out \
358 --extract \
359 --file=${finalAttrs.src} \
360 --strip-components=1 \
361 --wildcards '*'/include/plugin-api.h
362 '';
363 };
364
365 meta = {
366 description = "Tools for manipulating binaries (linker, assembler, etc.)";
367 longDescription = ''
368 The GNU Binutils are a collection of binary tools. The main
369 ones are `ld' (the GNU linker) and `as' (the GNU assembler).
370 They also include the BFD (Binary File Descriptor) library,
371 `gprof', `nm', `strip', etc.
372 '';
373 homepage = "https://www.gnu.org/software/binutils/";
374 license = lib.licenses.gpl3Plus;
375 maintainers = with lib.maintainers; [
376 ericson2314
377 lovesegfault
378 ];
379 platforms = lib.platforms.unix;
380
381 # INFO: Give binutils a lower priority than gcc-wrapper to prevent a
382 # collision due to the ld/as wrappers/symlinks in the latter.
383 priority = 10;
384 };
385})