Debian: Install More Recent LLVM/Clang Toolchain
That is, without creating a Frankendebian.
First, some important context: in wanting to compile some programs and
the Linux kernel[1] with Clang's Control Flow Integrity[2] feature, I was
made aware that one would need Clang 16 or higher, which means that the
versions in the official Debian repo (14 and 15) weren't going to work.
Programs would segfault, while the option wouldn't even show in the menu
when configuring the kernel (despite make oldconfig
asking if it should
be enabled).
Since I like testing security features, switching to version 16 right away was a no breainer. If none of this is a concern to you, then the versions in the official Debian repo will serve you just as well.
Enter apt.llvm.org
Although it's normally my preferred option, I wasn't willing to wait hours for a new LLVM version to compile on that day (I'm not the most patient man on earth), but luckily, the LLVM project maintains their own Debian (and Ubuntu) repos for that purpose.
Here, version 16 will be installed as it's the one identified as "stable" on the apt.llvm.org website, but version 17 (or any future ones) can be installed instead by swapping all instances of 16 with the desired number in the steps below.
Adding the repository
First, as the root user, create a deb822-style sources file in /etc/apt/sources.list.d/
called llvm.sources
:
https://raw.githubusercontent.com/RagnarokOS/src/master/etc/apt/sources.list.d/llvm.sources
Types: deb deb-src
URIs: http://apt.llvm.org/bookworm/
Suites: llvm-toolchain-bookworm-16
Components: main
Signed-by: /etc/apt/trusted.gpg.d/apt.llvm.org.asc
Next, download (as a non-root user) the project's gpg key with curl or wget:
$ wget https://apt.llvm.org/llvm-snapshot.gpg.key
- or -
$ curl -OL https://apt.llvm.org/llvm-snapshot.gpg.key
Then, as root, create the /etc/apt/trusted.gpg.d/apt.llvm.org.asc file:
# cat llvm-snapshot.gpg.key > /etc/apt/trusted.gpg.d/apt.llvm.org.asc
Afterwards, simply run apt update. If everything was properly set up, no errors will be reported.
And now, brace yourself.
Installing the packages
As root:
# apt-get install -y libllvm16 llvm-16 llvm-16-dev llvm-16-runtime \
clang-16 clang-tools-16 libclang-common-16-dev libclang-16-dev \
libclang1-16 clang-format-16 python3-clang-16 clang-tidy-16 \
libclang-rt-16-dev libpolly-16-dev libfuzzer-16-dev lldb-16 \
lld-16 libc++-16-dev libc++abi-16-dev libomp-16-dev \
libunwind-16-dev libmlir-16-dev flang-16
Note that this does not install absolutely everything, but close to it. So far, I have not come across a situation where something was missing, but if you do, refer to the "install" sections at apt.llvm.org.
Proper set-up with update-alternatives
This step is required in order to build the Linux kernel with the full toolchain properly, and mandatory if you want to use LLVM/Clang as the default toolchain.
# export VERSION="16"
# update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-"${VERSION}" 100 \
--slave /usr/bin/llvm-ar llvm-ar /usr/bin/llvm-ar-"${VERSION}" \
--slave /usr/bin/llvm-as llvm-as /usr/bin/llvm-as-"${VERSION}" \
--slave /usr/bin/llvm-bcanalyzer llvm-bcanalyzer /usr/bin/llvm-bcanalyzer-"${VERSION}" \
--slave /usr/bin/llvm-cov llvm-cov /usr/bin/llvm-cov-"${VERSION}" \
--slave /usr/bin/llvm-diff llvm-diff /usr/bin/llvm-diff-"${VERSION}" \
--slave /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-"${VERSION}" \
--slave /usr/bin/llvm-dwarfdump llvm-dwarfdump /usr/bin/llvm-dwarfdump-"${VERSION}" \
--slave /usr/bin/llvm-extract llvm-extract /usr/bin/llvm-extract-"${VERSION}" \
--slave /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-"${VERSION}" \
--slave /usr/bin/llvm-mc llvm-mc /usr/bin/llvm-mc-"${VERSION}" \
--slave /usr/bin/llvm-nm llvm-nm /usr/bin/llvm-nm-"${VERSION}" \
--slave /usr/bin/llvm-objdump llvm-objdump /usr/bin/llvm-objdump-"${VERSION}" \
--slave /usr/bin/llvm-ranlib llvm-ranlib /usr/bin/llvm-ranlib-"${VERSION}" \
--slave /usr/bin/llvm-readobj llvm-readobj /usr/bin/llvm-readobj-"${VERSION}" \
--slave /usr/bin/llvm-rtdyld llvm-rtdyld /usr/bin/llvm-rtdyld-"${VERSION}" \
--slave /usr/bin/llvm-size llvm-size /usr/bin/llvm-size-"${VERSION}" \
--slave /usr/bin/llvm-stress llvm-stress /usr/bin/llvm-stress-"${VERSION}" \
--slave /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-"${VERSION}" \
--slave /usr/bin/llvm-tblgen llvm-tblgen /usr/bin/llvm-tblgen-"${VERSION}" \
--slave /usr/bin/llvm-objcopy llvm-objcopy /usr/bin/llvm-objcopy-"${VERSION}" \
--slave /usr/bin/llvm-strip llvm-strip /usr/bin/llvm-strip-"${VERSION}"
# update-alternatives --install /usr/bin/clang clang /usr/bin/clang-"${VERSION}" 100 \
--slave /usr/bin/clang++ clang++ /usr/bin/clang++-"${VERSION}" \
--slave /usr/bin/asan_symbolize asan_symbolize /usr/bin/asan_symbolize-"${VERSION}" \
--slave /usr/bin/clang-cpp clang-cpp /usr/bin/clang-cpp-"${VERSION}" \
--slave /usr/bin/clang-cl clang-cl /usr/bin/clang-cl-"${VERSION}" \
--slave /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-"${VERSION}" \
--slave /usr/bin/lld lld /usr/bin/lld-"${VERSION}" \
--slave /usr/bin/lld-link lld-link /usr/bin/lld-link-"${VERSION}" \
--slave /usr/bin/clang-format clang-format /usr/bin/clang-format-"${VERSION}" \
--slave /usr/bin/clang-format-diff clang-format-diff /usr/bin/clang-format-diff-"${VERSION}" \
--slave /usr/bin/clang-include-fixer clang-include-fixer /usr/bin/clang-include-fixer-"${VERSION}" \
--slave /usr/bin/clang-offload-bundler clang-offload-bundler /usr/bin/clang-offload-bundler-"${VERSION}" \
--slave /usr/bin/clang-query clang-query /usr/bin/clang-query-"${VERSION}" \
--slave /usr/bin/clang-rename clang-rename /usr/bin/clang-rename-"${VERSION}" \
--slave /usr/bin/clang-reorder-fields clang-reorder-fields /usr/bin/clang-reorder-fields-"${VERSION}" \
--slave /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-"${VERSION}" \
--slave /usr/bin/lldb lldb /usr/bin/lldb-"${VERSION}" \
--slave /usr/bin/lldb-server lldb-server /usr/bin/lldb-server-"${VERSION}"
Using LLVM/Clang as the default toolchain (Optional)
If you wish to set LLVM/Clang as your default toolchain, use update-alternatives to set the proper symlinks.
Do so at your own risks, and note that programs which use automake/autotools (e.g.
coreutils, e2fsprogs, util-linux) will still set CC as gcc (and it'll work just
fine) so you'll need to specify that CC=clang
when running ./configure
.
# update-alternatives --install /usr/bin/cc cc /usr/bin/clang-"${VERSION}" 100
# update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld-"${VERSION}" 100
# update-alternatives --install /usr/bin/ar ar /usr/bin/llvm-ar-"${VERSION}" 100
# update-alternatives --install /usr/bin/ranlib ranlib /usr/bin/llvm-ranlib-"${VERSION}" 100
# update-alternatives --install /usr/bin/objcopy objcopy /usr/bin/llvm-objcopy-"${VERSION}" 100
# update-alternatives --install /usr/bin/strip strip /usr/bin/llvm-strip-"${VERSION}" 100
Scripting the process
If you would like to do the whole process (minus the repo setup) in one go, you can use this simple script, after making sure it doesn't do anything malicious, of course.
https://github.com/IanLeCorbeau/dotfiles/blob/master/.local/bin/llvm-default.sh
Links
[1] https://clang.llvm.org/docs/ControlFlowIntegrity.html
[2] https://github.com/RagnarokOS/kernel-build/releases