Home     Blog     Rss     Contact     Donate

Debian: Install More Recent LLVM/Clang Toolchain


December 10, 2024 update: It appears that Clang 16 is, in fact, available in Debian Bookworm. I highly doubt it got randomly added to the repo since I wrote this post, so the most likely explanation is that I missed it due to not paying attention. Either way, this does not invalidate this post. LLVM/Clang 17 and above are not available in Bookworm, and whatever version comes out after Trixie becomes stable will also not be available there, so there you go. Now, on to the original post.

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 17 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 17 will be installed but version 18 (or higher) can be installed instead by swapping all instances of 17 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-17
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 libllvm17 llvm-17 llvm-17-dev llvm-17-runtime \
    clang-17 clang-tools-17 libclang-common-17-dev libclang-17-dev \
    libclang1-17 clang-format-17 python3-clang-17 clang-tidy-17 \
    libclang-rt-17-dev libpolly-17-dev libfuzzer-17-dev lldb-17 \
    lld-17 libc++-17-dev libc++abi-17-dev libomp-17-dev \
    libunwind-17-dev libmlir-17-dev flang-17
    		

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="17"

# 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