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