Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nixification #81

Closed
piegamesde opened this issue Jul 6, 2021 · 22 comments · May be fixed by #86
Closed

Nixification #81

piegamesde opened this issue Jul 6, 2021 · 22 comments · May be fixed by #86

Comments

@piegamesde
Copy link

piegamesde commented Jul 6, 2021

As soon as I read that mold uses Gentoo for testing because it's source-based, I instantly thought about Nix being a perfect fit for it. Achieving this however turned out to be a lot harder than I initially thought, because the bootstrapping code is rather complex compared to the simple overrides one usually does.

Nevertheless, here's a file that builds any selected packages (list at the bottom) and all their transitive dependencies using mold as links (if they used bfd before). (Edit: updated file here)

let
  # Pin some fairly new nixpkgs
  sources = builtins.fetchTarball {
    name = "nixpkgs-unstable-2021-07-06";
    url = "https://github.com/nixos/nixpkgs/archive/291b3ff5af268eb7a656bb11c73f2fe535ea2170.tar.gz";
    sha256 = "1z2l7q4cmiaqb99cd8yfisdr1n6xbwcczr9020ss47y2z1cn1x7x";
  };

  # For bootstrapping
  pkgs0 = import sources {
    overlays = [
      (pkgs: super: {
        # TODO replace with proper packaging once https://github.com/NixOS/nixpkgs/pull/128889 is merged
        mold = pkgs.stdenv.mkDerivation {
          pname = "mold";
          version = "0.9.1";
          src = pkgs.fetchgit {
            url = "https://github.com/rui314/mold";
            rev = "v0.9.1";
            sha256 = "sha256-yIkW6OCXhlHZ1jC8/yMAdJbSgY9K40POT2zWv6wYr5E=";
          };
          nativeBuildInputs = with pkgs; [ clang_12 cmake lld_12 tbb xxHash zlib openssl git ];
          dontUseCmakeConfigure = "true";
          buildPhase = "make -j $NIX_BUILD_CORES";
          installPhase = "mkdir -p $out $out/bin $out/share/man/man1 && PREFIX=$out make install";
        };

        binutils_mold = pkgs.wrapBintoolsWith {
          bintools = pkgs.binutils-unwrapped.overrideAttrs (old: {
            postInstall = ''
              rm $out/bin/ld.gold
              rm $out/bin/ld.bfd
              ln -sf ${pkgs.mold}/bin/mold $out/bin/ld.bfd
            '';
          });
        };
        
        stdenv_mold = super.overrideCC super.stdenv (super.wrapCCWith rec {
          cc = super.gcc-unwrapped;
          bintools = pkgs.binutils_mold;
        });
      })
    ];
  };

  # Actual nixpkgs with patched linker in all packages
  pkgs = import sources {
    overlays = [
      (pkgs: super: {
        stdenv = pkgs0.stdenv_mold;
        mold = pkgs0.mold;
      })
    ];
  };
in with pkgs;
  # Packages we want to build
  linkFarmFromDrvs "packages-with-mold" [
    mold
    binutils
    stdenv
    opencv
    jack2
    chromium
  ]

Let me know what you think.

CC @nitsky

@rui314
Copy link
Owner

rui314 commented Jul 6, 2021

I'm not familiar with Nix, so bear with me if I ask silly questions. What is the point of using Nix instead of Dockerized Gentoo? Can I avoid using Docker if I adopt Nix?

@nitsky
Copy link

nitsky commented Jul 6, 2021

@piegamesde I am not familiar enough with the structure of nixpkgs to comment authoritatively. However, from reading it over, I see that you are patching stdenv and binutils to use mold as the linker, which sounds right. What about those packages that explicitly opt in to using lld such as mold itself? Scroll to the bottom of https://github.com/NixOS/nixpkgs/pull/128889/files#diff-ab5748dc9567516fefba8344056b51ec1866adeace380f46e58a7af3d619ea22R13818.

@piegamesde
Copy link
Author

What is the point of using Nix instead of Dockerized Gentoo?

Well, mainly because I can :) There probably are some advantages, but I don't know a lot about Gentoo so I can't really compare. Here are some things that are cool about Nix, you judge if Gentoo+Docker can do this too:

  • Fully reproducible builds + sandboxed environment
  • All tests of the packages are automatically run as part of the build process
  • Automatic bootstrapping if required, including for cross compilation
  • Automatic cross compilation (however a lot of packages are not well tested, so expect to do upstream fixes)
  • Pure: if you change something, all its reverse dependencies will be re-built
  • The way I've written the code, the changed linker affects the packages plus their transitive closure (which is huge, try nix-tree $(nix-instantiate mold.nix)). But there is also the possibility to only change the linker for the package itself.

Can I avoid using Docker if I adopt Nix?

Yes. You only need to have Nix installed.

What about those packages that explicitly opt in to using lld such as mold itself?

They won't use mold. This holds of all packages that have a custom stdenv (look for .override { stdenv = …; } in all-packages.nix). I could try and start to override other stdenvs to increase coverage.


I'm still trying to build the above derivation. A few packages are already failing. I'll have to disable mold and check that they aren't broken upstream, but otherwise I may have found some bugs?

Also, brainstorming some ideas how to move this prototype forward:

  • Replacing in with pkgs; … with in pkgs at the bottom would allow to build some packages ad-hoc: nix-build mold.nix -A chromium. This might be interesting for reproducible bug reports
  • The file could be part of the CI. We'd start with only a few packages that work and then incrementally add things as they get fixed. It'd catch all regressions. The trouble is, I'm not sure if GitHub will grant us enough CI compute power for free :)

@piegamesde
Copy link
Author

Note so self: the following packages fail with mold

┃ ⚠ valgrind-3.16.1 on ssh://builder1 failed with exit code 2 after ⏲ 00:01:03
┃ ⚠ mariadb-connector-c-3.1.12 on ssh://builder1 failed with exit code 2 after ⏲ 00:00:19
┃ ⚠ kexec-tools-2.0.20 on ssh://builder1 failed with exit code 2 after ⏲ 00:00:13
┃ ⚠ python3.8-python-lz4-3.1.3 on ssh://builder1 failed with exit code 137 after ⏲ 00:01:12
┃ ⚠ llvm-11.1.0 on ssh://builder1 failed with exit code 2 after ⏲ 00:55:42
┃ ⚠ llvm-12.0.0 on ssh://builder1 failed with exit code 2 after ⏲ 01:30:25
┃ ⚠ gmp-6.2.1 on ssh://builder1 failed with exit code 2 after ⏲ 00:01:07
┃ ⚠ pixman-0.38.4 on ssh://builder1 failed with exit code 2 after ⏲ 00:00:43
┃ ⚠ python3.8-cffi-1.14.5 on ssh://builder1 failed with exit code 1 after ⏲ 00:06:41
┃ ⚠ glibc-locales-2.32-48 on ssh://builder1 failed with exit code 1 after ⏲ 00:00:09

For comparison, without mold:

┃ ⚠ python3.8-python-lz4-3.1.3 on ssh://builder1 failed with exit code 137 after ⏲ 00:01:07
┃ ⚠ rustc-1.53.0 on ssh://builder1 failed with exit code 2 after ⏲ 00:43:21
┃ ⚠ gfortran-9.3.0 on ssh://builder1 failed with exit code 2 after ⏲ 00:14:59

The difference should be subject to closer investigation (and I'll open bug reports on nixpkgs for the latter).

@rui314
Copy link
Owner

rui314 commented Jul 8, 2021

valgrind's failure is known -- valgrind assumes some specific section layout which mold doesn't comply. Technically, it's not mold's fault, and I think I need to submit a patch to valgrind to fix it. Other failures are not known.

@piegamesde
Copy link
Author

I did some further research, filtered out false positives. The following packages have interesting failures that are very likely directly caused by mold: kexec-tools, pixman, glibc-locales, mariadb-connector-c. And the following packages fail too, but with less obvious error messages: gfortran, gmp, python3.8-cffi

@nehaljwani
Copy link
Contributor

mariadb-connector-c (version script support is incomplete?):

mold: /path/to/mariadb-connector-c-3.1.12-src/build/libmariadb/mariadbclient.def:1: VERSION {
                                                                                    ^ unknown token

pixman (something off with 'common' symbols: being treated as undefined?):

[nix-shell:~/t/pixman-0.38.4/test]$ gcc -fopenmp -g -O2 -Wall -Wdeclaration-after-statement -Wno-unused-local-typedefs -fno-strict-aliasing -fvisibility=hidden -pthread -g -O2 -Wall -Wdeclaration-after-statement -Wno-unused-local-typedefs -fno-strict-aliasing -fvisibility=hidden -fopenmp -pthread -o .libs/region-test region-test.o  ./.libs/libutils.a ../pixman/.libs/libpixman-1.so -L/nix/store/66gafbj2zjhsqamy9k1cslisdgr16asn-libpng-apng-1.6.37/lib -L/nix/store/vh5k4xa2zk5l5yrbppa4xjilqmb79ncp-zlib-1.2.11/lib /nix/store/66gafbj2zjhsqamy9k1cslisdgr16asn-libpng-apng-1.6.37/lib/libpng16.so -lz -lm -pthread -fopenmp -Wl,-rpath -Wl,/nix/store/66gafbj2zjhsqamy9k1cslisdgr16asn-libpng-apng-1.6.37/lib 
mold: undefined symbol: region-test.o: prng_state
mold: undefined symbol: region-test.o: prng_state
mold: undefined symbol: region-test.o: prng_state_data
collect2: error: ld returned 1 exit status

[nix-shell:~/t/pixman-0.38.4/test]$ ar t ./.libs/libutils.a
utils.o
utils-prng.o

[nix-shell:~/t/pixman-0.38.4/test]$ nm utils.o | grep prng_state
0000000000000008 C prng_state
0000000000000050 C prng_state_data

kexectools (no support in mold for -Text=<> or --section-start):

[nix-shell:~/t/kexec-tools-2.0.20]$ make -j V=1
mkdir -p build/lib/kexec-tools
ld -melf_i386 -e _start -Ttext 0x10000 -o build/lib/kexec-tools/kexec_test kexec_test/kexec_test16.o kexec_test/kexec_test.o
mold: cannot open text
make: *** [kexec_test/Makefile:38: build/lib/kexec-tools/kexec_test] Error 1

glibcLocales (some assumptions in configure script break):

checking version of /nix/store/5v3fydcprprsvyd0d84q29pj2z5d9x1l-gcc-wrapper-10.3.0/bin/ld... v. ?.??, bad
*** These critical programs are missing or too old: GNU ld
*** Check the INSTALL file for required versions.

gmp (in tests/mpf, make check fails for 'reuse' which throws a floating-point exception!)

../../test-driver: line 107: 15884 Floating point exception"$@" > $log_file 2>&1
FAIL: reuse

gfortran: fails because it can't build gmp as a dependency

python3.pkgs.cffi (not sure if related to mold):

========================================================================================================== short test summary info ===========================================================================================================
FAILED testing/cffi0/test_ffi_backend.py::TestFFI::test_sin - OSError: ctypes.util.find_library() did not manage to locate a library called 'm'
FAILED testing/cffi0/test_ffi_backend.py::TestFFI::test_sinf - OSError: ctypes.util.find_library() did not manage to locate a library called 'm'
FAILED testing/cffi0/test_ffi_backend.py::TestFFI::test_dlopen_flags - OSError: ctypes.util.find_library() did not manage to locate a library called 'm'
FAILED testing/cffi0/test_ffi_backend.py::TestFFI::test_dlopen_constant - OSError: ctypes.util.find_library() did not manage to locate a library called 'm'

@rui314
Copy link
Owner

rui314 commented Jul 9, 2021

mariadb-connector-c is failing because mold doesn't support the VERSION directive in a linker script. I'll add that to mold. However, mariadb also uses other linker script features that mold doesn't support, so even with that change mariadb won't be able to be built with mold.

If pixman is failing due to common symbols in an archive file, it is hard to fix. It looks like BFD linker's common symbol handling is not consistent and highly dependent on the order of files in the command line, and it is hard to mimic the exact behavior. Even gold behaves slightly different from BFD, and so do LLVM lld. It's usually pretty easy to fix an application (you can just compile with -fno-common, which is considered a good practice in general).

As to kexectools, let me implement -Ttext.

@nehaljwani
Copy link
Contributor

The gmp test failure is worth looking into as well. The floating point exception goes away (quite strange!) on using the bfd linker.

@rui314
Copy link
Owner

rui314 commented Jul 9, 2021

How can I build gmp with mold on Nix? I just tried building the most recent version of gmp using mold on my Ubuntu, but the tests just pass, so I think I have to built it on Nix.

@nehaljwani
Copy link
Contributor

To build gmp with nix:

sudo docker run --rm -it nixos/nix /bin/sh
vi mold.nix  # Copy the contents from the first comment and replace the list at the end with just mold and gmp
nix-build mold.nix

@piegamesde
Copy link
Author

@rui314 I've changed the nix file a bit to make it easier to build and test individual packages:

let
  # Pin some fairly new nixpkgs
  sources = builtins.fetchTarball {
    name = "nixpkgs-unstable-2021-07-06";
    url = "https://github.com/nixos/nixpkgs/archive/291b3ff5af268eb7a656bb11c73f2fe535ea2170.tar.gz";
    sha256 = "1z2l7q4cmiaqb99cd8yfisdr1n6xbwcczr9020ss47y2z1cn1x7x";
  };

  # For bootstrapping
  pkgs = import sources {
    overlays = [
      (pkgs: super: {
        # TODO replace with proper packaging once https://github.com/NixOS/nixpkgs/pull/128889 is merged
        mold = pkgs.stdenv.mkDerivation {
          pname = "mold";
          version = "0.9.1";
          src = pkgs.fetchgit {
            url = "https://github.com/rui314/mold";
            rev = "v0.9.1";
            sha256 = "sha256-yIkW6OCXhlHZ1jC8/yMAdJbSgY9K40POT2zWv6wYr5E=";
          };
          nativeBuildInputs = with pkgs; [ clang_12 cmake lld_12 tbb xxHash zlib openssl git ];
          dontUseCmakeConfigure = "true";
          buildPhase = "make -j $NIX_BUILD_CORES";
          installPhase = "mkdir -p $out $out/bin $out/share/man/man1 && PREFIX=$out make install";
        };

        binutils_mold = pkgs.wrapBintoolsWith {
          bintools = pkgs.binutils-unwrapped.overrideAttrs (old: {
            postInstall = ''
              rm $out/bin/ld.gold
              rm $out/bin/ld.bfd
              ln -sf ${pkgs.mold}/bin/mold $out/bin/ld.bfd
            '';
          });
        };
        
        stdenv_mold = super.overrideCC super.stdenv (super.wrapCCWith rec {
          cc = super.gcc-unwrapped;
          bintools = pkgs.binutils_mold;
        });
      })
    ];
  };

  # Actual nixpkgs with patched linker in all packages
  pkgsMold = import sources {
    overlays = [
      (self: super: {
        stdenv = pkgs.stdenv_mold;
        mold = pkgs.mold;
      })
    ];
  };
in {
  inherit pkgs;
  inherit pkgsMold;

  # Use -A testPackages to build all of these
  testPackages = with pkgsMold; linkFarmFromDrvs "packages-with-mold" [
    # mold
    binutils
    stdenv
    #opencv
    jack2
    chromium
  ];
}

You can now do nix-build mold.nix -A pkgsMold.gmp and also nix-build mold.nix -A pkgs.gmp for a baseline comparison.

I'm planning to add a pkgsMoldLight that only uses mold for linking the package itself and not for its transitive dependencies, so that we can test chromium and other big boys before all its dependencies work.

rui314 added a commit that referenced this issue Jul 10, 2021
Previously, a GOT relocation (e.g. R_X86_64_REX_GOTPCRELX) and a
R_X86_64_64 relocation referring the same imported symbols were
resolved to different addresses. Here is why:

 - When we saw a R_X86_64_64 relocation against an imported symbol,
   we created a PLT and resolve the relocation there.

 - GOT relocation is resolved to a GOT entry, which has a true
   address of an imported function at runtime, which is different
   from PLT entries that redirect calls to the real function.

With this patch, we no longer create a PLT entry for R_X86_64_64.
Instead, we emit a dynamic relocation so that it is always resolved
to a real function address.

Fixes GNU MP's `make check` failure, which was reported at
#81
@rui314
Copy link
Owner

rui314 commented Jul 10, 2021

Thanks guys for teaching me how to use Nix!

@piegamesde That config file works for me, and I succeeded to reproduce the test failure of GNU MP. It was due to a subtle bug in mold, and I submitted a fix.

@ghost
Copy link

ghost commented Jul 10, 2021

Hello! 👋 Thank you for working on this project!

Rather coincidentally, I have recently decided to write a package for mold for GNU Guix.

I have never used Nix before, but I know people say it’s similar to Guix. (Indeed, Guix is inspired by Nix, at least from what the developers say.)

I came across this issue, and decided to try to do something similar for Guix, to see how well it would work, mostly for fun. It actually ended up going fairly well, honestly!

Using my package, I was able to write the following file:

(define-module (mold-ld))

(use-modules
  (guix packages)
  (guix build-system trivial))

(include "packages/compilers/mold.scm")

(define-public mold-ld
  (package
    (name "mold-ld")
    (version (package-version mold))
    (synopsis (package-synopsis mold))
    (home-page (package-home-page mold))
    (description (package-description mold))
    (license (package-license mold))
    (inputs `(("mold" ,mold)))
    (source #f)
    (build-system trivial-build-system)
    (arguments
      '(#:builder
         (let ((out (assoc-ref %outputs "out")) (mold (assoc-ref %build-inputs "mold")))
           (begin
             (mkdir out)
             (mkdir (string-append out "/bin"))
             (symlink (string-append mold "/bin/mold") (string-append out "/bin/ld"))))))))

The file is fairly staright forward, it simply creates a new package that links its /bin/ld to my mold package’s /bin/mold.

Using this file, I was able to run a build of some projects using mold with the guix build command very easily!

edit mold-ld.scm # use a text editor editor and paste that file here
git clone https://github.com/zamfofex/packages
guix build -L. --with-input=ld-wrapper=mold-ld hello binutils jack2 ungoogled-chromium

Unfortunately, it failed while building binutils. GNU hello worked, though, and I was able to verify it was indeed using mold with readelf. The reason it failed to build binutils seems unrelated — I’m going to try to updgrade my packages and try again, and hopefully things work better.

binutils build output

Here’s the last couple lines of the output of the failed binutils build anyway, in case it’s relevant or useful. For some reason, it seems to be a problem regarding ncurses while building documentation for texinfo.

make[4]: Leaving directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7/gnulib/lib'
make[3]: Leaving directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7/gnulib/lib'
make[2]: Leaving directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7/gnulib/lib'
Making all in info
make[2]: Entering directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7/info'
gcc -DHAVE_CONFIG_H -I. -I..  -I.. -I../gnulib/lib -I../gnulib/lib -DLOCALEDIR=\"/gnu/store/b4ajjyp74756g97qc9svfcpd2ddspasa-texinfo-6.7/share/locale\" -DINFODIR=\"/gnu/store/b4ajjyp74756g97qc9svfcpd2ddspasa-texinfo-6.7/share/info\" -DINFODIR2=\"/gnu/store/b4ajjyp74756g97qc9svfcpd2ddspasa-texinfo-6.7/share/info\"   -g -O2 -MT makedoc.o -MD -MP -MF .deps/makedoc.Tpo -c -o makedoc.o makedoc.c
mv -f .deps/makedoc.Tpo .deps/makedoc.Po
gcc  -g -O2   -o makedoc makedoc.o ../gnulib/lib/libgnu.a -lncurses
rm -f doc.c funs.h
..//info/makedoc ./session.c ./echo-area.c ./infodoc.c ./m-x.c ./indices.c ./nodemenu.c ./footnotes.c ./variables.c
..//info/makedoc: error while loading shared libraries: libncursesw.so.6: cannot open shared object file: No such file or directory
make[2]: *** [Makefile:2640: funs.h] Error 127
make[2]: Leaving directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7/info'
make[1]: *** [Makefile:1224: all-recursive] Error 1
make[1]: Leaving directory '/tmp/guix-build-texinfo-6.7.drv-0/texinfo-6.7'
make: *** [Makefile:1163: all] Error 2
command "make" "-j" "4" failed with status 2
builder for `/gnu/store/niramrp01l6m8r1s1hn1k9zx511miy7h-texinfo-6.7.drv' failed with exit code 1
build of /gnu/store/niramrp01l6m8r1s1hn1k9zx511miy7h-texinfo-6.7.drv failed
View build log at '/var/log/guix/drvs/ni/ramrp01l6m8r1s1hn1k9zx511miy7h-texinfo-6.7.drv.bz2'.
cannot build derivation `/gnu/store/ihcfrsqwdw8lxky5fl7pz74314ykdjs3-binutils-2.34.drv': 1 dependencies couldn't be built
guix build: error: build of `/gnu/store/ihcfrsqwdw8lxky5fl7pz74314ykdjs3-binutils-2.34.drv' failed

@rui314
Copy link
Owner

rui314 commented Jul 10, 2021

@zamfofex Awesome! This is honestly the first time for me to hear about Guix, but I'm happy to hear that mold could be used with that package system. I used to write lots of Scheme code at one point of my life, so it is interesting in that sense too.

I'm not sure why binutils is failing to be built with mold. I'll try to reproduce the issue locally.

@rui314
Copy link
Owner

rui314 commented Jul 10, 2021

@zamfofex I installed guix using the installer script as explained in https://guix.gnu.org/manual/en/html_node/Binary-Installation.html. I then executed the following commands as you instructed:

cat > mold-ld.scm # paste the scm file
git clone https://github.com/zamfofex/packages
guix build -L. --with-input=ld-wrapper=mold-ld hello

This is what I got:

ruiu@blue:/tmp/guix$ guix build -L. --with-input=ld-wrapper=mold-ld hello
WARNING: (mold-ld): `openssl' imported from both (guix licenses) and (gnu packages tls)
WARNING: (mold-ld): `zlib' imported from both (guix licenses) and (gnu packages compression)
error: package: unbound variable
hint: Did you forget `(use-modules (guix packages))'?

WARNING: (#{ g486}#): `openssl' imported from both (guix licenses) and (gnu packages tls)
WARNING: (#{ g486}#): `zlib' imported from both (guix licenses) and (gnu packages compression)
guix build: warning: failed to load '(packages compilers chibicc)':
no code for module (packages compilers chibicc)
hint: File `./packages/compilers/chibicc.scm' should probably start with:

     (define-module (packages compilers chibicc))

WARNING: (#{ g488}#): `openssl' imported from both (guix licenses) and (gnu packages tls)
WARNING: (#{ g488}#): `zlib' imported from both (guix licenses) and (gnu packages compression)
guix build: warning: failed to load '(packages compilers mold)':
no code for module (packages compilers mold)
hint: File `./packages/compilers/mold.scm' should probably start with:

     (define-module (packages compilers mold))

guix build: warning: failed to load '(packages games liquidwar5)':
no code for module (packages games liquidwar5)
hint: File `./packages/games/liquidwar5.scm' should probably start with:

     (define-module (packages games liquidwar5))

WARNING: (#{ g490}#): `openssl' imported from both (guix licenses) and (gnu packages tls)
guix build: warning: failed to load '(packages historical kde1)':
no code for module (packages historical kde1)
hint: File `./packages/historical/kde1.scm' should probably start with:

     (define-module (packages historical kde1))

WARNING: (#{ g491}#): `openssl' imported from both (guix licenses) and (gnu packages tls)
guix build: warning: failed to load '(packages irc glirc)':
no code for module (packages irc glirc)
hint: File `./packages/irc/glirc.scm' should probably start with:

     (define-module (packages irc glirc))

WARNING: (mold-ld): `openssl' imported from both (guix licenses) and (gnu packages tls)
WARNING: (mold-ld): `zlib' imported from both (guix licenses) and (gnu packages compression)
ice-9/eval.scm:223:20: In procedure proc:
error: gcc-11: unbound variable
hint: Did you forget a `use-modules' form?

What am I missing?

@piegamesde
Copy link
Author

I added a pkgsMoldLight variant that only uses mold for linking the application itself, keeping the dependencies as they are:

let
  # Pin some fairly new nixpkgs
  sources = builtins.fetchTarball {
    name = "nixpkgs-unstable-2021-07-06";
    url = "https://github.com/nixos/nixpkgs/archive/291b3ff5af268eb7a656bb11c73f2fe535ea2170.tar.gz";
    sha256 = "1z2l7q4cmiaqb99cd8yfisdr1n6xbwcczr9020ss47y2z1cn1x7x";
  };

  # For bootstrapping
  pkgs = import sources {
    overlays = [
      (pkgs: super: {
        # TODO replace with proper packaging once https://github.com/NixOS/nixpkgs/pull/128889 is merged
        mold = pkgs.stdenv.mkDerivation {
          pname = "mold";
          version = "0.9.1";
          src = pkgs.fetchgit {
            url = "https://github.com/rui314/mold";
            rev = "v0.9.1";
            sha256 = "sha256-yIkW6OCXhlHZ1jC8/yMAdJbSgY9K40POT2zWv6wYr5E=";
          };
          nativeBuildInputs = with pkgs; [ clang_12 cmake lld_12 tbb xxHash zlib openssl git ];
          dontUseCmakeConfigure = "true";
          buildPhase = "make -j $NIX_BUILD_CORES";
          installPhase = "mkdir -p $out $out/bin $out/share/man/man1 && PREFIX=$out make install";
        };

        binutils_mold = pkgs.wrapBintoolsWith {
          bintools = pkgs.binutils-unwrapped.overrideAttrs (old: {
            postInstall = ''
              rm $out/bin/ld.gold
              rm $out/bin/ld.bfd
              ln -sf ${pkgs.mold}/bin/mold $out/bin/ld.bfd
            '';
          });
        };
        
        stdenv_mold = super.overrideCC super.stdenv (super.wrapCCWith rec {
          cc = super.gcc-unwrapped;
          bintools = pkgs.binutils_mold;
        });
      })
    ];
  };

  # Actual nixpkgs with patched linker in all packages
  pkgsMold = import sources {
    overlays = [
      (self: super: {
        stdenv = pkgs.stdenv_mold;
        mold = pkgs.mold;
      })
    ];
  };
  
  # nixpkgs version where all packages are linked with mold individually, but not their transitive dependencies
  pkgsMoldLight = import sources {
    overlays = [
      # This is really hacky in quite a few ways, but it works
      (self: super:
      let
        overridePackage = name: let
          package = pkgs.${name};
        in
          if package ? override && (builtins.hasAttr "stdenv" package.override.__functionArgs) then
            (package.override { stdenv = pkgs.stdenv_mold; })
          else
            package;

        moldPackages = pkgs.lib.trivial.pipe pkgs [
          pkgs.lib.attrNames
          (map (name: pkgs.lib.nameValuePair name (overridePackage name)))
          builtins.listToAttrs
        ];
      in
        moldPackages
      )
    ];
  };
in {
  inherit pkgs pkgsMold pkgsMoldLight;

  # Packages we want to build
  testPackages = with pkgsMold; linkFarmFromDrvs "packages-with-mold" [
    # mold
    binutils
    stdenv
    #opencv
    jack2
    chromium
  ];
  # Packages we already know that work in order to catch regressions
  ciPackages = with pkgsMold; linkFarmFromDrvs "packages-with-mold" [
    # TODO
  ];
}

Sadly, it doesn't really catch the things I'd hoped it would: For example, chromium gets no override at all, because it internally uses llvmPackages instead. And the failing test in pkgsMold.gmp suddenly works in pkgsMoldLight.gmp. Which means that this probably isn't a viable solution for catching issues.

@rui314
Copy link
Owner

rui314 commented Jul 10, 2021

@piegamesde Building not only a specified package but its all dependencies with mold isn't bad to find as many bugs as possible. The only catch is, if there's a package that can't be built with mold, all other packages that depend on it will also transitively fail. But we can workaround it by writing a simple shim that wraps mold. The shim invokes ld.bfd instead of mold only when it is used to build a known-broken package.

@ghost
Copy link

ghost commented Jul 10, 2021

What am I missing?

@rui314: I think you might need to run guix package -u first. If that doesn’t work, try running guix pull and then guix package -u again.

I’m going to try to execute it on a fresh Guix VM to see what’s needed, if there are additional steps, I can share them here too.

@ghost
Copy link

ghost commented Jul 10, 2021

A little update about this: I was able to build binutils without its dependencies. I’m going to try to figure out what the issue could be with texinfo, and maybe also try other projects like Chromium or Jack. It should also be possible to use the default ld-wrapper for a limited given list of packages, as you suggest.

Though maybe this should move to a different issue, instead of resuing this one for Guix too? It’s up to you, really.

information about building binutils

The binutils build (without dependencies) went fairly smoothly. All I had to do was add the following to the end of file I shared:

(define moldify (options->transformation '((with-input . "ld-wrapper=mold-ld"))))

(moldify binutils)

But I also had to add (guix transformations) and (gnu packages base) to the use-module clause. The whole file should look something like this:

the whole file
(define-module (mold-ld))

(use-modules
  (guix packages)
  (guix build-system trivial)
  (guix transformations)
  (gnu packages base))

(include "packages/compilers/mold.scm")

(define-public mold-ld
  (package
    (name "mold-ld")
    (version (package-version mold))
    (synopsis (package-synopsis mold))
    (home-page (package-home-page mold))
    (description (package-description mold))
    (license (package-license mold))
    (inputs `(("mold" ,mold)))
    (source #f)
    (build-system trivial-build-system)
    (arguments
      '(#:builder
         (let ((out (assoc-ref %outputs "out")) (mold (assoc-ref %build-inputs "mold")))
           (begin
             (mkdir out)
             (mkdir (string-append out "/bin"))
             (symlink (string-append mold "/bin/mold") (string-append out "/bin/ld"))))))))

(define moldify (options->transformation '((with-input . "ld-wrapper=mold-ld"))))

(moldify binutils)

From there, I built the modified binutils package using guix build -L. -f mold-ld.scm (still with a clone of my repository on the same directory).

I was able to use the built readelf on itself to verify that it was indeed using mold! (Which was kinda fun.)

$ env -S /gnu/store/r7yba33ig6ljlcfq1xp0hzmirq86jdkz-binutils-2.34/bin/readelf{' -p .comment',}

String dump of section '.comment':
  [     1]  mold 0.9.1 (compatible with GNU ld and GNU gold)
  [    32]  GCC: (GNU) 7.5.0

@piegamesde
Copy link
Author

@zamfofex Yeah, I'm starting to get confused which comments are referring to Nix and which ones are based on Guix, so a separate issue might be a good idea.

@rui314
Copy link
Owner

rui314 commented Dec 23, 2021

Since Nix package is already available, I'm closing this issue.

@rui314 rui314 closed this as completed Dec 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants