8.34. LLVM final 22.1.3
Build and install the final native LLVM, Clang, LLD, compiler-rt, libunwind, libcxxabi, and libcxx toolchain inside the target chroot.
Input assumption: llvm-project-22.1.3.src.tar.xz is already present in /sources from the chapter 4 source staging step.
Licenses
- Apache-2.0 WITH LLVM-exception
Dependencies
- musl (libc)
- cmake
- samurai or ninja
- zlib-ng (libz)
- zstd
- clang
- lld
- compiler-rt
- libunwind
- libcxxabi
- libcxx
LLVM is a modular compiler infrastructure project. we need it to provide the final native compiler backend, linker integration, binary utilities, and toolchain libraries for the target system.
Clang is a C and C++ frontend for LLVM. we need it to provide the final clang, clang++, cc, and c++ compilers used by the completed system.
LLD is LLVM's linker. we need it to provide the final ld.lld linker and the default ld compatibility entry point.
compiler-rt is LLVM's runtime support library project. we need it to provide Clang builtins, CRT objects, and low-level compiler runtime support.
libunwind is a stack unwinding library. we need it to provide unwind support used by C++ exception handling.
libcxxabi is the LLVM C++ ABI support library. we need it to provide ABI and exception runtime support for libcxx.
libcxx is the LLVM C++ standard library implementation. we need it to provide the final C++ standard library used by Clang.
Extract Sources and Enter the LLVM Build Root
cd /sources
rm -rf llvm-project-22.1.3.src
tar -xf llvm-project-22.1.3.src.tar.xz
cd llvm-project-22.1.3.src/llvm
Configure LLVM, Clang, and LLD
This pass builds the final native compiler and linker. The runtimes are built in separate standalone steps afterward so that compiler-rt, libunwind, libcxxabi, and libcxx land in the book's custom filesystem layout predictably.
lbi_cmake build-final \
-G Ninja \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS="$LWI_CFLAGS -fPIC" \
-DCMAKE_CXX_FLAGS="$LWI_CXXFLAGS -fPIC" \
-DCMAKE_EXE_LINKER_FLAGS="$LBI_CUSTOM_LDFLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="$LBI_CUSTOM_LDFLAGS" \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_INSTALL_BINUTILS_SYMLINKS=ON \
-DLLVM_TARGETS_TO_BUILD="host" \
-DCLANG_DEFAULT_CXX_STDLIB=libc++ \
-DCLANG_DEFAULT_LINKER=lld \
-DCLANG_DEFAULT_RTLIB=compiler-rt \
-DCLANG_DEFAULT_UNWINDLIB=none \
-DLLVM_ENABLE_ZLIB=ON \
-DLLVM_ENABLE_ZSTD=ON \
-DLLVM_ENABLE_LIBXML2=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_BUILD_TESTS=OFF \
-DLLVM_INCLUDE_DOCS=OFF \
-DLLVM_BUILD_LLVM_DYLIB=ON \
-DLLVM_LINK_LLVM_DYLIB=ON \
-DLLVM_ENABLE_RTTI=ON \
-DCLANG_CONFIG_FILE_SYSTEM_DIR=/system/configuration/clang \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DCMAKE_BUILD_TYPE=Release
Build LLVM, Clang, and LLD
cmake --build build-final $LWI_MAKE_FLAGS
Install LLVM, Clang, and LLD
cmake --install build-final
Add Final Clang Driver Wrapper Defaults
The final system uses a non-FHS layout. These wrappers make Clang find the target headers, startup objects, compiler-rt files, and C++ runtime libraries without every later package needing custom include and linker flags.
mv /system/binaries/clang /system/binaries/clang.real
mv /system/binaries/clang++ /system/binaries/clang++.real
cat > /system/binaries/clang <<EOF
#!/bin/sh
exec /system/binaries/clang.real \\
--target="$LBI_TARGET" \\
-isystem /system/headers \\
-B/system/libraries \\
-B/system/libraries/clang/22/lib/linux \\
-B/system/libraries/clang/22/lib/linux \\
-L/system/libraries \\
-Wno-unused-command-line-argument \\
"\$@"
EOF
cat > /system/binaries/clang++ <<EOF
#!/bin/sh
exec /system/binaries/clang++.real \\
--target="$LBI_TARGET" \\
-nostdinc++ \\
-I/system/headers/c++/v1 \\
-isystem /system/libraries/clang/22/include \\
-isystem /system/libraries/clang/22/include \\
-isystem /system/headers \\
-B/system/libraries \\
-B/system/libraries/clang/22/lib/linux \\
-B/system/libraries/clang/22/lib/linux \\
-L/system/libraries \\
-Wl,-rpath,/system/libraries \\
-Wno-unused-command-line-argument \\
"\$@" \\
-lc++ \\
-lc++abi \\
-lunwind
EOF
chmod 755 /system/binaries/clang /system/binaries/clang++
cd /system/binaries
ln -sf clang cc
ln -sf clang++ c++
ln -sf clang "$LBI_TARGET-clang"
ln -sf clang++ "$LBI_TARGET-clang++"
ln -sf clang "$LBI_TARGET-cc"
ln -sf clang++ "$LBI_TARGET-c++"
Build and Install compiler-rt Builtins and CRT Objects
:::pull-note Compiler-rt note: this section builds only the builtins and CRT objects. Sanitizers, libFuzzer, profiling, XRay, ORC, and MemProf are disabled because this book stage only needs the baseline runtime support required by the compiler driver.
cd /sources/llvm-project-22.1.3.src/compiler-rt
lbi_cmake build-compiler-rt-final \
-G Ninja \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_ASM_COMPILER=clang \
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
-DCMAKE_C_FLAGS="$LWI_CFLAGS" \
-DCMAKE_CXX_FLAGS="$LWI_CXXFLAGS" \
-DCMAKE_ASM_FLAGS="$LWI_CFLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="$LBI_CUSTOM_LDFLAGS" \
-DCOMPILER_RT_INSTALL_PATH=/system/lib/clang/22 \
-DCOMPILER_RT_BUILD_BUILTINS=ON \
-DCOMPILER_RT_BUILD_CRT=ON \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCOMPILER_RT_BUILD_MEMPROF=OFF \
-DCOMPILER_RT_BUILD_ORC=OFF \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
-DCOMPILER_RT_BUILD_XRAY=OFF \
-DCOMPILER_RT_INCLUDE_TESTS=OFF \
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-compiler-rt-final --target builtins $LWI_MAKE_FLAGS
cmake --build build-compiler-rt-final --target crt $LWI_MAKE_FLAGS
cmake --install build-compiler-rt-final
Build and Install LLVM C++ Runtimes
cd /sources/llvm-project-22.1.3.src/runtimes
lbi_cmake build-runtimes-final \
-G Ninja \
-DCMAKE_C_COMPILER=clang.real \
-DCMAKE_CXX_COMPILER=clang++.real \
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
-DCMAKE_C_FLAGS="$LWI_CFLAGS" \
-DCMAKE_CXX_FLAGS="$LWI_CXXFLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="$LBI_CUSTOM_LDFLAGS" \
-DLLVM_ENABLE_RUNTIMES="libunwind;libcxxabi;libcxx" \
-DLLVM_ENABLE_LIBXML2=OFF \
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_INCLUDE_DOCS=OFF \
-DLIBUNWIND_INSTALL_LIBRARY_DIR=/system/libraries \
-DLIBUNWIND_INCLUDE_TESTS=OFF \
-DLIBUNWIND_INCLUDE_DOCS=OFF \
-DLIBCXXABI_INSTALL_LIBRARY_DIR=/system/libraries \
-DLIBCXXABI_INCLUDE_TESTS=OFF \
-DLIBCXX_INSTALL_LIBRARY_DIR=/system/libraries \
-DLIBCXX_INCLUDE_TESTS=OFF \
-DLIBCXX_INCLUDE_BENCHMARKS=OFF \
-DLIBCXX_INCLUDE_DOCS=OFF \
-DLIBCXX_HAS_MUSL_LIBC=ON \
-DLIBCXX_HAS_ATOMIC_LIB=OFF \
-DLIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL=OFF \
-DLIBCXXABI_USE_LLVM_UNWINDER=ON \
-DLIBCXX_USE_COMPILER_RT=ON \
-DLIBCXXABI_USE_COMPILER_RT=ON \
-DLIBUNWIND_USE_COMPILER_RT=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-runtimes-final $LWI_MAKE_FLAGS
cmake --install build-runtimes-final
Add libgcc Compatibility Links
LLVM's llvm-libgcc runtime exists for distributions that replace GCC runtime pieces with compiler-rt and libunwind while still needing the traditional libgcc.a, libgcc_eh.a, and libgcc_s.so names. The final system already built compiler-rt builtins and libunwind separately, so this step installs a small helper that creates those compatibility names in /system/libraries.
cat > /system/binaries/lbi-refresh-libgcc-links <<'EOF'
#!/bin/sh
set -eu
libdir=/system/libraries
arch=${LBI_ARCH:-$(uname -m)}
case "$arch" in
x86_64|amd64) compiler_rt_arch=x86_64 ;;
i?86) compiler_rt_arch=i386 ;;
aarch64|arm64) compiler_rt_arch=aarch64 ;;
*) compiler_rt_arch=$arch ;;
esac
builtins=$({ find "$libdir/clang" /system/lib/clang \
-type f -name "libclang_rt.builtins-${compiler_rt_arch}.a" 2>/dev/null || true; } | head -n1)
unwind_static=$({ find "$libdir" \
-maxdepth 1 -type f -name 'libunwind.a' 2>/dev/null || true; } | head -n1)
if [ -e "$libdir/libunwind.so" ]; then
unwind_shared=$libdir/libunwind.so
else
unwind_shared=$({ find "$libdir" \
-maxdepth 1 \( -type f -o -type l \) -name 'libunwind.so*' 2>/dev/null || true; } | head -n1)
fi
if [ -z "$builtins" ]; then
echo "libclang_rt.builtins archive for $compiler_rt_arch was not found" >&2
exit 1
fi
if [ -z "$unwind_static" ]; then
echo "libunwind.a was not found" >&2
exit 1
fi
if [ -z "$unwind_shared" ]; then
echo "libunwind.so was not found" >&2
exit 1
fi
ln -sf "$builtins" "$libdir/libgcc.a"
ln -sf "$(basename "$unwind_static")" "$libdir/libgcc_eh.a"
ln -sf "$(basename "$unwind_shared")" "$libdir/libgcc_s.so.1.0"
ln -sf libgcc_s.so.1.0 "$libdir/libgcc_s.so.1"
ln -sf libgcc_s.so.1 "$libdir/libgcc_s.so"
EOF
chmod 755 /system/binaries/lbi-refresh-libgcc-links
/system/binaries/lbi-refresh-libgcc-links
Normalize Clang Runtime Layout
Some LLVM runtime builds install files under /system/lib/clang, while the book's final compiler wrapper and library layout use /system/libraries. Create compatibility links for the resource files and CRT objects.
mkdir -p /system/libraries/clang/22/lib/linux
mkdir -p /system/lib/clang/22/lib/linux
if [ -d /system/lib/clang/22 ]; then
cp -R /system/lib/clang/22/* /system/libraries/clang/22/ 2>/dev/null || true
fi
if [ -d /system/libraries/clang/22 ]; then
cp -R /system/libraries/clang/22/* /system/lib/clang/22/ 2>/dev/null || true
fi
CRTBEGIN_OBJ=$(find /system/libraries/clang /system/lib/clang \
-type f \( -name 'crtbeginS.o' -o -name 'clang_rt.crtbegin*.o' \) 2>/dev/null | head -n1)
CRTEND_OBJ=$(find /system/libraries/clang /system/lib/clang \
-type f \( -name 'crtendS.o' -o -name 'clang_rt.crtend*.o' \) 2>/dev/null | head -n1)
if [ -n "$CRTBEGIN_OBJ" ] && [ -n "$CRTEND_OBJ" ]; then
CRT_DIR=$(dirname "$CRTBEGIN_OBJ")
ln -sf "$(basename "$CRTBEGIN_OBJ")" "$CRT_DIR/crtbeginS.o"
ln -sf "$(basename "$CRTEND_OBJ")" "$CRT_DIR/crtendS.o"
ln -sf "$CRTBEGIN_OBJ" /system/libraries/crtbeginS.o
ln -sf "$CRTEND_OBJ" /system/libraries/crtendS.o
fi
Verify the Final Toolchain
Check the compiler driver versions.
clang --version
clang++ --version
ld.lld --version
Check C compilation and linking.
cat > /tmp/lbi-c-test.c <<'EOF'
#include <stdio.h>
int main(void)
{
puts("c ok");
return 0;
}
EOF
cc /tmp/lbi-c-test.c -o /tmp/lbi-c-test
/tmp/lbi-c-test
Check C++ header ordering, libc++ wrapper headers, exception runtime support, and the final C++ link line.
cat > /tmp/lbi-cxx-test.cpp <<'EOF'
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <new>
struct test_value {
int value;
};
int main()
{
auto item = std::make_unique<test_value>();
item->value = 123;
std::cout << item->value << std::endl;
return errno;
}
EOF
c++ /tmp/lbi-cxx-test.cpp -o /tmp/lbi-cxx-test
/tmp/lbi-cxx-test
The C++ test should print:
123
Command Explanations
rm -rf llvm-project-...andtar -xf: Recreate a clean LLVM source tree.lbi_cmake build-final -G Ninja: Configures the final LLVM build with the book's layout and Ninja generator.-DCMAKE_C_COMPILER=clangand-DCMAKE_CXX_COMPILER=clang++: Builds with the current target compiler wrappers.-DCMAKE_C_FLAGSand-DCMAKE_CXX_FLAGS: Apply local tuning and position-independent code where required.-DLLVM_ENABLE_PROJECTS,LLVM_TARGETS_TO_BUILD, and default Clang runtime options: Select the final compiler, linker, target, and runtime defaults.cmake --build build-final $LWI_MAKE_FLAGSandcmake --install build-final: Build and install the final LLVM toolchain.mv clang clang.realandcat > clang: Preserve real compiler binaries and install wrapper scripts with the book's default include/library paths.lbi_cmake build-compiler-rt-final: Builds compiler-rt builtins and CRT objects against the final compiler.lbi_cmake build-runtimes-final: Rebuilds libunwind, libcxxabi, and libcxx for the final compiler/runtime layout.cat > /system/binaries/lbi-refresh-libgcc-links: Installs a helper that refreshes libgcc-compatible links to compiler-rt outputs.cp -R /system/lib/clang/22/* ...: Mirrors Clang resource files between expected resource-directory layouts.clang --version, compile tests, andreadelf: Verify the final C and C++ compiler drivers and dynamic interpreter behavior.