Skip to content

Instantly share code, notes, and snippets.

@MawKKe
Last active July 23, 2024 07:25
Show Gist options
  • Save MawKKe/b8af6c1555f1c7aa4c2760350ed97fff to your computer and use it in GitHub Desktop.
Save MawKKe/b8af6c1555f1c7aa4c2760350ed97fff to your computer and use it in GitHub Desktop.
How to try out the new "mold" linker with your CMake C/C++ project

How to try out the new mold linker with your CMake C/C++ project

Here is a quick how-to if you want to try out the new (supposedly fast) C/C++ linker https://github.com/rui314/mold

In this document I used Ubuntu-22.04 docker container with

  • GCC version 11.2.0
  • Clang version 14.0.0

1. Install mold :

You have three options:

  • a) Install mold from a package repository (apt install mold). Note: typically repository versions are pretty old compared to upstream releases. At the time of this writing (2022-07-03) Ubuntu repo provides v1.0.3, while newest Github release is v1.3.1.
  • b) Download, extract and install a release from Github
  • c) Clone the repo and build/install from source. Note: you need a relatively new compiler with C++20 support.

Pick whatever option you are most comfortable with. Just make sure the mold executable is in your path:

$ mold --version
mold 1.3.1 (71c4c9092271312bcc05724c8ca707487c31bab1; compatible with GNU ld)

You are now ready to configure your own project to use the new linker

2. Configure a CMake project to use mold as linker:

In short: to enable mold for linking, you specify a custom linker via compiler flag -fuse-ld. The flag is supported by both Clang and GCC (at least with the versions mentioned in the intro).

Note that when I say "compiler flag", it is actually needed during the link step only. It just so happens that the modern GCC/Clang toolchains perform linking by invoking the compiler (as a wrapper), instead of calling the linker directly.

Anyways, to enable mold as a linker, add the following to your CMake configuration command:

-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold"

For example:

$ cmake \
    -B mybuild \
    -G Ninja \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" \
    -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" \
    ~/path/to/myproject

The first option applies when linking executables, the latter when linking shared libraries. You may omit either one to suit your needs.

(Next you might ask why is there no variable for specifying -fuse-ld=mold for static libraries?? Well, the answer is that when static libraries are being built, there is no actual link step; the resulting .a libraries are just archives of loosely related compiled object files (.o). The actual linking happens at a later stage, possibly outside of your project.)

Next I will verify that the linker is actually used:

In the above demo project I have the following targets defined in CMakeLists.txt:

...
add_executable(helloworld-exe helloworld.cc)
add_library(myshared SHARED myshared.cc)
...

Now I build the project the usual way:

$ cmake --build mybuild --verbose --target helloworld-exe --target myshared
[1/4] /usr/bin/clang++ -Dmyshared_EXPORTS  -fPIC -MD -MT CMakeFiles/myshared.dir/myshared.cc.o -MF CMakeFiles/myshared.dir/myshared.cc.o.d -o CMakeFiles/myshared.dir/myshared.cc.o -c myshared.cc
[2/4] /usr/bin/clang++ -MD -MT CMakeFiles/helloworld-exe.dir/helloworld.cc.o -MF CMakeFiles/helloworld-exe.dir/helloworld.cc.o.d -o CMakeFiles/helloworld-exe.dir/helloworld.cc.o -c helloworld.cc
[3/4] : && /usr/bin/clang++ -fuse-ld=mold CMakeFiles/helloworld-exe.dir/helloworld.cc.o -o helloworld-exe   && :
[4/4] : && /usr/bin/clang++ -fPIC -fuse-ld=mold -shared -Wl,-soname,libmyshared.so -o libmyshared.so CMakeFiles/myshared.dir/myshared.cc.o   && :

..and we notice the -fuse-ld=mold being passed to the compiler, but only for the (final) link step in both cases.

I can also verify that mold is being used by inspecting the comment section, for example in helloworld-exe:

$ readelf -p .comment mybuild/bin/helloworld-exe

String dump of section '.comment':
    [     0]  GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0
    [    26]  mold 1.3.1 (71c4c9092271312bcc05724c8ca707487c31bab1; compatible with GNU ld)
    [    75]  Ubuntu clang version 14.0.0-1ubuntu1

Tips

  • Since the flag -fuse-ld is compiler-specific, you really should not hard-code it in your CMakeLists.txt files. And only pass it on the command line if you know that your compiler supports it. Non-Clang/GCC compilers may have similar flag, but but the principle (and CMake variables used) should remain the same.

  • Don't put something like add_link_options(-fuse-ld=mold) in your CMakeLists.txt file. You wouldn't hardcode a compiler this way, would you? This kind of information is passed from outside of the project during configuration stage - your project should remain compiler and linker agnostic as much as possible (or at least you should not assume a specific compiler or linker is used).

  • Don't attempt to pass the flag via CMAKE_CXX_FLAGS or similar. It will result in annoying warnings during compilation since the flag is not actually needed/used during compilation, but only during linking.

  • Avoid Setting CMAKE_CXX_LINK_FLAGS or CMAKE_C_LINK_FLAGS. They work, but will only apply for linking C/C++ executables, not shared libraries.

  • CMake supports other languages besides C and C++. Using CMAKE_EXE_LINKER_FLAGS and CMAKE_SHARED_LINKER_FLAGS allows you to specify the linker for all targets regardless of language. Although this may cause problems in a mixed language project where not all tools understand the same flag(s). But if you are utilizing only GNU-ish compiler ecosystems (GCC, Clang, GNU Fortran, ...) you can safely pass -fuse-ld to all of them via CMAKE_EXE_LINKER_FLAGS / CMAKE_SHARED_LINKER_FLAGS variables alone.

  • Don't attempt to use CMAKE_LINKER. Some online forums may recommend you to set CMAKE_LINKER=/path/to/mold. However, it is (presumably) some kind of legacy variable and is not supported anymore. In my experiments the variable stopped working during transition from CMake version 3.18 -> 3.22. So, just forget it altogether.

  • You may have came across this Stackoverflow question where the top answer recommends changing CMAKE_CXX_LINK_EXECUTABLE. However, this is rather heavy-handed (and error prone) way of accomplishing the intended effect. And again, it applies to executables only. Don't do this. See CMake source code to get an idea what the change would do.

  • Also, don't attempt to set -DCMAKE_CXX_LINK_EXECUTABLE=/path/to/mold - it does not work the way you expect it to. See, CMake source code to get an idea what the variable should contain.

  • If you are using VScode, put the option(s) in your settings.json for simplified usage:

      "cmake.configureSettings": {
          ...
          "CMAKE_C_COMPILER": "clang",
          "CMAKE_CXX_COMPILER": "clang++",
          "CMAKE_EXE_LINKER_FLAGS": "-fuse-ld=mold",
          "CMAKE_SHARED_LINKER_FLAGS": "-fuse-ld=mold"
          ...
      }
    

    and then reconfigure your project via the command palette.

Contributing

This document was written by Markus H (MawKKe).

Feel free to give feedback/improvement suggestions at https://gist.github.com/MawKKe/b8af6c1555f1c7aa4c2760350ed97fff

See also https://github.com/MawKKe and https://gist.github.com/MawKKe for other interesting stuff.

Please note that I am not the author of mold. I'm just a friendly neighbourhood C++ programmer.

@Spixmaster
Copy link

Spixmaster commented Mar 26, 2024

It is now even easier with CMake 3.29.0. Just set the according variable, https://cmake.org/cmake/help/latest/variable/CMAKE_LINKER_TYPE.html.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment