Musl Based LFS pt. 2 -- Base system

If you have not alreay read the last blog do that first. This blog post assumes you already have a working chroot and bootable system in qemu.

The packages you should have are

The goal of this blog post is to try and get a working llvm setup or decided if it’s not worth it and go with gcc.

If you just want to test it out you can download this image

# download the disk image
wget https://dakotajkeeler.com/download/disk-002.img.xz

# Extract the image
xz -d disk-002.img.xz

# run the image
qemu-system-x86_64 -cpu host -enable-kvm -m 4G -drive file=disk-002.img,format=raw -nographic -serial mon:stdio

# Run bash instead of sh as the default shell and source /root/.bashrc to setup basic enviroment. 
bash
source /root/.bashrc

Testing clang out

# These were later added to the blog post to fix issues but never made it into the disk-002.img
ln -svf clang /usr/bin/cc 
ln -svf clang++ /usr/bin/c++

cd /packages/vim-9.1.1166
make clean
./configure 
make

# run our vim
src/vim

Setup environment

If you followed the last blog post you should have $HOME/mlfs. Verify that it exist and if so run the following commands to setup your envs.

export MLFS=$HOME/mlfs
export ROOTFS=$HOME/mlfs/rootfs
export MLFS_TGT=x86_64-unknown-linux-musl

Get some packages

cd $MLFS/packages
wget --no-clobber https://ftp.gnu.org/gnu/autoconf/autoconf-2.72.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/automake/automake-1.17.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/make/make-4.4.1.tar.gz
wget --no-clobber https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-21.1.8.tar.gz
wget --no-clobber https://github.com/libffi/libffi/releases/download/v3.4.7/libffi-3.4.7.tar.gz
wget --no-clobber https://download.gnome.org/sources/libxml2/2.14/libxml2-2.14.1.tar.xz
wget --no-clobber https://zlib.net/fossils/zlib-1.3.1.tar.gz
wget --no-clobber https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz
wget --no-clobber https://github.com/vim/vim/archive/v9.1.1166/vim-9.1.1166.tar.gz
wget --no-clobber https://www.kernel.org/pub/linux/utils/util-linux/v2.40/util-linux-2.40.4.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/patch/patch-2.7.6.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/gzip/gzip-1.13.tar.xz
wget --no-clobber https://ftp.gnu.org/gnu/gettext/gettext-0.24.tar.xz
wget --no-clobber https://www.cpan.org/src/5.0/perl-5.40.1.tar.xz
wget --no-clobber https://www.python.org/ftp/python/3.13.2/Python-3.13.2.tar.xz
wget --no-clobber https://www.sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz
wget --no-clobber https://github.com//tukaani-project/xz/releases/download/v5.6.4/xz-5.6.4.tar.xz
wget --no-clobber https://sourceforge.net/projects/libuuid/files/libuuid-1.0.3.tar.gz
git clone https://github.com/chimera-linux/libatomic-chimera.git
wget --no-clobber https://ftp.gnu.org/gnu/tar/tar-1.35.tar.xz

Build environment

Just like before, were going to set some envs. These might be changed locally and then reset.

  export CC="clang"
  export CXX="clang++"
  export CFLAGS="-fPIC -std=gnu99 --sysroot=$ROOTFS -Wno-old-style-definition -I$ROOTFS/usr/include -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -Wno-error=implicit-function-declaration"
  export CXXFLAGS="-fPIC"
  export LDFLAGS="--sysroot=$ROOTFS -Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1"
  export PKG_CONFIG_SYSROOT_DIR="$ROOTFS"

libff

#prep sourcee 
cd $MLFS/packages
tar -xf libffi-3.4.7.tar.gz 
cd libffi-3.4.7

./configure --prefix=$ROOTFS/usr   \
            --build=$(./build-aux/config.guess) \
            --host=$MLFS_TGT

make
make  install

Zlib

#prep sourcee 
cd $MLFS/packages
tar -xf zlib-1.3.1.tar.gz
cd zlib-1.3.1

./configure --prefix=$ROOTFS/usr

make
make  install

Gettext

Slightly following the wisdom of the LFS book were prebuilding some tools for temp system. That way once were in the chroot we’ll have the proper deps nescerary to rebuild the system.

#prep sourcee 
cd $MLFS/packages
tar -xf gettext-0.24.tar.xz
cd gettext-0.24

./configure --prefix=$ROOTFS/usr --disable-shared

make
make install

Bison

#prep sourcee 
cd $MLFS/packages
tar -xf bison-3.8.2.tar.xz
cd bison-3.8.2

./configure --prefix=$ROOTFS/usr \
            --docdir=$ROOTFS/usr/share/doc/bison-3.8.2

make
make install

Perl

#prep sourcee 
cd $MLFS/packages
tar -xf perl-5.40.1.tar.xz
cd perl-5.40.1

localedef -i C -f UTF-8 C.UTF-8

sh Configure -des                                         \
        -D prefix=$ROOTFS/usr                               \
        -D vendorprefix=$ROOTFS/usr                         \
        -D useshrplib                                \
        -D privlib=$ROOTFS/usr/lib/perl5/5.40/core_perl     \
        -D archlib=$ROOTFS/usr/lib/perl5/5.40/core_perl     \
        -D sitelib=$ROOTFS/usr/lib/perl5/5.40/site_perl     \
        -D sitearch=$ROOTFS/usr/lib/perl5/5.40/site_perl    \
        -D vendorlib=$ROOTFS/usr/lib/perl5/5.40/vendor_perl \
        -D vendorarch=$ROOTFS/usr/lib/perl5/5.40/vendor_perl

make
make install

Bzip

#prep sourcee 
cd $MLFS/packages
tar -xf bzip2-1.0.8.tar.gz
cd bzip2-1.0.8

# Bzip doesn't pick up enviroment variables. You must pass them in.
make CFLAGS="$CFLAGS" CC="$CC" LDFLAGS="$LDFLAGS"
make PREFIX=$ROOTFS/usr install


ln -vfs libbz2.so.1.0.8 $ROOTFS/usr/lib/libbz2.so
for i in $ROOTFS/usr/bin/{bzcat,bunzip2}; do
    ln -sfv bzip2 $i
done

# I noticed some of my symlinks still had the absolute path in them. This fixes it.
ln -sfv bzdiff $ROOTFS/usr/bin/bzcmp
ln -sfv bzgrep $ROOTFS/usr/bin/bzegrep
ln -sfv bzgrep $ROOTFS/usr/bin/bzfgrep
ln -sfv bzmore $ROOTFS/usr/bin/bzless

Xz

#prep sourcee 
cd $MLFS/packages
tar -xf xz-5.6.4.tar.xz 
cd xz-5.6.4

./configure --prefix=$ROOTFS/usr                     \
            --host=$MLFS_TGT                   \
            --build=$(build-aux/config.guess)
make
make install

zstd

#prep sourcee 
cd $MLFS/packages
tar -xf zstd-1.5.7.tar.gz 
cd zstd-1.5.7

make prefix=$ROOTFS/usr
make prefix=$ROOTFS/usr install

libuuid

Ideally I shouldn’t be using this library because of how old it is and the fact it’s not being maintined. However, this was what it took to get python to build for me.

#prep sourcee 
cd $MLFS/packages
tar -xf libuuid-1.0.3.tar.gz 
cd libuuid-1.0.3

./configure --prefix=$ROOTFS/usr                     \
            --host=$MLFS_TGT                   \
            --build=$(build-aux/config.guess)
make
make install

Util-Linux

#prep sourcee 
cd $MLFS/packages
tar -xf util-linux-2.40.4.tar.xz
cd util-linux-2.40.4

mkdir -vp $ROOTFS/var/lib/hwclock
mkdir -vp $ROOTFS/usr/share/doc/util-linux-2.40.4

./configure --libdir=$ROOTFS/usr/lib     \
            --runstatedir=$ROOTFS/run    \
            --prefix=$ROOTFS    \
            --disable-chfn-chsh   \
            --host=$MLFS_TGT \
            --disable-login       \
            --disable-nologin     \
            --disable-su          \
            --disable-setpriv     \
            --disable-runuser     \
            --disable-pylibmount  \
            --disable-static      \
            --disable-liblastlog2 \
            --without-python      \
            --without-udev        \
            ADJTIME_PATH=/var/lib/hwclock/adjtime 

  make
  make install

Libxml2

#prep sourcee 
cd $MLFS/packages
tar -xf libxml2-2.14.1.tar.xz
cd libxml2-2.14.1

./configure --prefix=$ROOTFS/usr   \
            --build=$(./build-aux/config.guess) \
            --host=$MLFS_TGT \
            --without-python

make
make  install

gzip

#prep sourcee 
cd $MLFS/packages
tar -xf  gzip-1.13.tar.xz 
cd  gzip-1.13

./configure --prefix=$ROOTFS/usr --host=$MLFS_TGT

make
make  install

tar

#prep sourcee 
cd $MLFS/packages
tar -xf tar-1.35.tar.xz
cd tar-1.35

./configure --prefix=$ROOTFS/usr                     \
            --build=$(build-aux/config.guess) \
            --host=x86_64-linux-musl
make
make install

Libatomic-chimera

All of llvm/clang require libatomic.so.1 to exist by extentin of relying on libc++.so.1 and it doesn’t. So I’m going to see if I can hack it with this.

#prep sourcee 
cd $MLFS/packages
cd libatomic-chimera

make PREFIX=$ROOTFS/
make PREFIX=$ROOTFS/ install

make

#prep sourcee 
cd $MLFS/packages
tar -xf make-4.4.1.tar.gz 
cd make-4.4.1

./configure --prefix=$ROOTFS/usr                     \
            --build=$(build-aux/config.guess) \
            --host=x86_64-linux-musl
make
make install

autoconf

#prep sourcee 
cd $MLFS/packages
tar -xf autoconf-2.72.tar.xz 
cd autoconf-2.72

./configure --prefix=$ROOTFS/usr                     \
            --build=$(build-aux/config.guess) \
            --host=x86_64-linux-musl
make
make install

automake

#prep sourcee 
cd $MLFS/packages
tar -xf automake-1.17.tar.xz
cd automake-1.17

./configure --prefix=$ROOTFS/usr                     \
            --build=$(build-aux/config.guess) \
            --host=x86_64-linux-musl
make
make install

llvm-runtimes

Before we can attempt to out right build LLVM we first need our c++ runtimes.

#prep sourcee 
cd $MLFS/packages
tar -xf llvmorg-21.1.8.tar.gz
cd llvm-project-llvmorg-21.1.8

mkdir -p build-runtimes && cd build-runtimes

cmake -G "Unix Makefiles" ../runtimes \
    -DLLVM_ENABLE_RUNTIMES="compiler-rt;libunwind;libcxxabi;libcxx" \
    -DCMAKE_BUILD_TYPE=Release \
    -DLLVM_TARGETS_TO_BUILD="host" \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DCMAKE_ASM_COMPILER=clang \
    -DCMAKE_C_COMPILER_TARGET="$MLFS_TGT" \
    -DCMAKE_CXX_COMPILER_TARGET="$MLFS_TGT" \
    -DCMAKE_SYSROOT="$ROOTFS" \
    -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
    -DCMAKE_INSTALL_PREFIX=$ROOTFS/usr \
    -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON \
    -DLIBCXX_USE_COMPILER_RT=ON \
    -DLIBCXXABI_USE_COMPILER_RT=ON \
    -DLIBCXXABI_USE_LLVM_UNWINDER=ON \
    -DLIBUNWIND_USE_COMPILER_RT=ON \
    -DCOMPILER_RT_USE_BUILTINS_LIBRARY=ON \
    -DLLVM_INCLUDE_TESTS=OFF \
    -DCMAKE_CXX_FLAGS="--target=$MLFS_TGT -nostdinc++ -nostdlib++" \
    -DCOMPILER_RT_BUILD_SANITIZERS=OFF \
    -DCOMPILER_RT_BUILD_XRAY=OFF \
    -DCOMPILER_RT_BUILD_PROFILE=OFF \
    -DCOMPILER_RT_BUILD_MEMPROF=OFF \
    -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
    -DCOMPILER_RT_BUILD_ORC=OFF \
    -DLIBCXX_ENABLE_LOCALIZATION=ON \
    -DLIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL=OFF \
    -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
    -DLLVM_DEFAULT_TARGET_TRIPLE=$MLFS_TGT \
    -DLIBCXX_HAS_MUSL_LIBC=ON \
    -DCOMPILER_RT_BUILD_BUILTINS=ON \
    -DCMAKE_POSITION_INDEPENDENT_CODE=ON

  make
  make install

Testing our new c++ environment

If you cannot get this to work. Do not procced! Having a working c++ enviroment is an absolute must to continue with the project.

cat > hello.cpp <<'EOF'
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1,2,3};
    std::cout << "hello libc++: " << v.size() << "\n";
    return 0;
}
EOF



clang++ --target="$MLFS_TGT" --sysroot="$ROOTFS" \
  -nostdinc++ \
  -isystem "$ROOTFS/usr/include/$MLFS_TGT/c++/v1" \
  -isystem "$ROOTFS/usr/include/c++/v1" \
  -stdlib=libc++ \
  -L"$ROOTFS/usr/lib/$MLFS_TGT" -lc++ -lc++abi -lunwind \
  -rtlib=compiler-rt \
  -unwindlib=libunwind \
  -fuse-ld=lld \
  hello.cpp -o hello-libcxx


file hello-libcxx
readelf -d hello-libcxx | grep NEEDED

clang++ --target="$MLFS_TGT" --sysroot="$ROOTFS" \
  -nostdinc++ \
  -isystem "$ROOTFS/usr/include/$MLFS_TGT/c++/v1" \
  -isystem "$ROOTFS/usr/include/c++/v1" \
  -stdlib=libc++ \
  -L"$ROOTFS/usr/lib/$MLFS_TGT" \
  -rtlib=compiler-rt \
  -unwindlib=libunwind \
  -fuse-ld=lld \
  -static \
  -Wl,--start-group -lc++ -lc++abi -lunwind -lclang_rt.builtins -Wl,--end-group \
  hello.cpp -o hello-libcxx-static

  ldd hello-libcxx-static

“host” build of llvm

These tools need to be able to run on the host system during the cross build of our llvm/clang. Which depending on libc/archeticture there is no guarentee that they would run if you didn’t build them against the host system.

unset CFLAGS CXXFLAGS LDFLAGS CC CXX AR RANLIB LD
export CC=clang
export CXX=clang++

cd $MLFS/packages/llvm-project-llvmorg-21.1.8
mkdir -p build-host && cd build-host

cmake -G "Unix Makefiles" "../llvm" \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DLLVM_ENABLE_PROJECTS="clang;lld" \
    -DLLVM_INCLUDE_TESTS=OFF \
    -DLLVM_INCLUDE_BENCHMARKS=OFF \
    -DLLVM_INCLUDE_EXAMPLES=OFF \
    -DLLVM_TARGETS_TO_BUILD="X86;AVR" \
    -DLLVM_ENABLE_LIBATOMIC=OFF 
    

make -j$(nproc) llvm-tblgen clang-tblgen llvm-min-tblgen

set some c++ envs

This ensures that the system can find all the right libraries and header files.

export CC="clang"
export CXX="clang++"
export LD="ld.lld"
export CFLAGS="-fPIC -std=gnu99 --sysroot=$ROOTFS -Wno-old-style-definition -I$ROOTFS/usr/include -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -Wno-error=implicit-function-declaration"
 
export CXXFLAGS="--target=$MLFS_TGT --sysroot=$ROOTFS \
  -nostdinc++ \
  -isystem $ROOTFS/usr/include/$MLFS_TGT/c++/v1 \
  -isystem $ROOTFS/usr/include/c++/v1 \
  -stdlib=libc++ \
  -Wno-unused-command-line-argument \
  -fPIC"

export LDFLAGS="-fuse-ld=lld \
  -rtlib=compiler-rt \
  -unwindlib=libunwind \
  -stdlib=libc++ \
  -L$ROOTFS/usr/lib/$MLFS_TGT \
  -lc++ -lc++abi -lunwind \
  --sysroot=$ROOTFS \
  -Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1"

LLVM/Clang

#prep sourcee 
cd $MLFS/packages/llvm-project-llvmorg-21.1.8

mkdir -vp build 
cd build

# These are the only two platforms I care about. It took almost a couple hours building to build
# platforms I have zero interest in targetting.
#     -DLLVM_TARGETS_TO_BUILD="X86;AVR"

cmake -G "Unix Makefiles" "../llvm" \
    -DCMAKE_BUILD_TYPE=Release \
    -DLLVM_TARGETS_TO_BUILD="host" \
    -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DCMAKE_ASM_COMPILER=clang \
    -DCMAKE_C_COMPILER_TARGET="$MLFS_TGT" \
    -DCMAKE_CXX_COMPILER_TARGET="$MLFS_TGT" \
    -DCMAKE_SYSROOT="$ROOTFS" \
    -DCMAKE_INSTALL_PREFIX=$ROOTFS/usr \
    -DLLVM_ENABLE_PROJECTS="clang;lld" \
    -DLLVM_USE_HOST_TOOLS=ON \
    -DLLVM_TABLEGEN="$PWD/../build-host/bin/llvm-tblgen" \
    -DCLANG_TABLEGEN="$PWD/../build-host/bin/clang-tblgen" \
    -DLLVM_INCLUDE_TESTS=OFF \
    -DLLVM_INCLUDE_BENCHMARKS=OFF \
    -DLLVM_INCLUDE_EXAMPLES=OFF \
    -DCLANG_DEFAULT_CXX_STDLIB=libc++ \
    -DCLANG_DEFAULT_RTLIB=compiler-rt \
    -DCLANG_DEFAULT_UNWINDLIB=libunwind \
    -DLLVM_INSTALL_BINUTILS_SYMLINKS=ON \
    -DHAVE_MALLINFO=0 \ 
    -DHAVE_MALLINFO2=0 \
    -DHAVE_DECL_ARC4RANDOM=0 \
    -DHAVE_BACKTRACE=0 \
    -DLLVM_ENABLE_BACKTRACES=OFF \
    -DLLVM_ENABLE_UNWIND_TABLES=OFF \
    -DLLVM_ENABLE_LIBATOMIC=OFF
    

make -j$(nproc)
make install

Python

Since we are cross compiling python we need to add the –with-build-python and –disable-ipv6. The first option will require a python version realitivly close to the version you are building. – On slackware I had to build Python 3.13 to be able to do this.

We have to build this after LLVM so that it doesn’t try to run this. Because it’s not statically built so I will fail on the host system. I know what you’re going to say though. Just build it statically, I already have several hours into getting to build and run and how it will react when building llvm when I already got that process largly sorted out is a headache I don’t want today.

The build error out about these values So I hard code them to make them work. ac_cv_file__dev_ptmx=yes
ac_cv_file__dev_null=yes
ac_cv_file__dev_ptc=yes \

#prep sourcee 
cd $MLFS/packages
tar -xf Python-3.13.2.tar.xz
cd Python-3.13.2

ac_cv_file__dev_ptmx=yes \
ac_cv_file__dev_null=yes \
ac_cv_file__dev_ptc=yes  \
./configure --prefix=$ROOTFS/usr   \
            --enable-shared \
            --without-ensurepip \
            --host=$MLFS_TGT \
            --build=$(./config.guess) \
            --with-build-python \
            --disable-ipv6

make
make install

Chroot into the system

We need to copy over the packages were going to build first in the system

If you don’t delete $ROOTFS/packages you must make a larger disk.img before copying the $ROOTFS over.

mkdir -vp $ROOTFS/packages/ 
cp -apvr $MLFS/packages/vim-9.1.1166.tar.gz $ROOTFS/packages/

# IF it errors out with not such file: Did you skip the first blog post?
cp -apvr $MLFS/packages/ncurses-6.5.tar.gz $ROOTFS/packages/
sudo chroot $ROOTFS

Musl dynamic linker

We then need to do some work to make our programs work. Musl dynamic linker searchs the path in the /etc/ld-musl-x86_64.path If itt isn’t in there it won’t be loaded.

# Allow the dynamic loader to find our libstdc++ and related stuff
echo "/usr/lib/x86_64-unknown-linux-musl" >> /etc/ld-musl-x86_64.path
echo "/usr/lib" >> /etc/ld-musl-x86_64.path
echo "/lib" >> /etc/ld-musl-x86_64.path


# We need to symlink our ld.lld to ld
ln -sf ld.lld /usr/bin/ld

Test clang

Now that we’re in the chroot we need to make sure we can compile simple programs. In the LFS book they create a symlink cc that points to the system compiler.

ln -svf clang /usr/bin/cc 
ln -svf clang++ /usr/bin/c++

I ran into some problems with getting a simple hello.c program to compile. Bellow are the steps to fixing it.

clang hello.c
ld: error: cannot open crtbeginS.o: No such file or directory ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-gnu/libclang_rt.builtins.a: No such file or directory ld: error: unable to find library -lunwind ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-gnu/libclang_rt.builtins.a: No such file or directory ld: error: unable to find library -lunwind ld: error: cannot open crtendS.o: No such file or directory

# To fix this I created a global clang config file that sets some basic things like target and tell 
# to use compiler-rt and libunwind
echo "--target=x86_64-unknown-linux-musl \
--sysroot=/ \
-rtlib=compiler-rt \
-unwindlib=libunwind \
-stdlib=libc++" > /usr/bin/clang.cfg

ld: error: cannot open crtbeginS.o: No such file or directory ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-musl/libclang_rt.builtins.a: No such file or directory ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-musl/libclang_rt.builtins.a: No such file or directory ld: error: cannot open crtendS.o: No such file or directory

clang is looking for these files in a directory that does exist So I create it and symlink in the file it needs

mkdir -p /usr/lib/clang/21/lib/x86_64-unknown-linux-musl

ln -sv /usr/lib/x86_64-unknown-linux-musl/libclang_rt.builtins.a \
    /usr/lib/clang/21/lib/x86_64-unknown-linux-musl/

ld: error: cannot open crtbeginS.o: No such file or directory ld: error: cannot open crtendS.o: No such file or directory clang: error: linker command failed with exit code 1 (use -v to see invocation)

These files exist but have clang_rt. appended in front. So I created two sets of symlinks. One for each file in the directory they exist in. And another in the directory clang is looking for them in.

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtbegin.o \
    /usr/lib/x86_64-unknown-linux-musl/crtbeginS.o

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtend.o \
    /usr/lib/x86_64-unknown-linux-musl/crtendS.o

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtbegin.o \
    /usr/lib/clang/21/lib/x86_64-unknown-linux-musl/crtbeginS.o

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtend.o \
    /usr/lib/clang/21/lib/x86_64-unknown-linux-musl/crtendS.o   

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtbegin.o \
    /usr/lib/x86_64-unknown-linux-musl/crtbeginT.o

ln -sv /usr/lib/x86_64-unknown-linux-musl/clang_rt.crtend.o \
    /usr/lib/x86_64-unknown-linux-musl/crtend.o
    

After this building the simple c program worked! But not the hello.cpp….

clang++ hello.cpp In file included from hello.cpp:1: In file included from /usr/bin/../include/c++/v1/iostream:39: /usr/bin/../include/c++/v1/__config:13:10: fatal error: ‘__config_site’ file not found 13 | #include <__config_site> | ^~~~~~~~~~~~~~~ 1 error generated.

ln -sv /usr/include/x86_64-unknown-linux-musl/c++/v1/__config_site \
    /usr/include/c++/v1/__config_site

clang++ hello.cpp ld: error: cannot open crtbeginS.o: No such file or directory ld: error: unable to find library -lc++ ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-gnu/libclang_rt.builtins.a: No such file or directory ld: error: unable to find library -lunwind ld: error: cannot open /usr/lib/clang/21/lib/x86_64-unknown-linux-gnu/libclang_rt.builtins.a: No such file or directory ld: error: unable to find library -lunwind ld: error: cannot open crtendS.o: No such file or directory clang++: error: linker command failed with exit code 1 (use -v to see invo

echo "--target=x86_64-unknown-linux-musl \
-rtlib=compiler-rt \
-unwindlib=libunwind \
-stdlib=libc++ \
-L/usr/lib/x86_64-unknown-linux-musl" > /usr/bin/clang++.cfg

Now hello.cpp bellow should also build. – I’m leaving these errors in here because my work around seems very hacky. That way if anybody has any better ideas or see a mistake I’ve made that would elimate these problems I would love to know.

cat > hello.cpp <<'EOF'
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1,2,3};
    std::cout << "hello libc++: " << v.size() << "\n";
    return 0;
}
EOF

cat > hello.c <<'EOF'
#include <stdio.h>

int main() {
    printf("Hello world!\n");
    return 0;
}
EOF


Ncurses

Now that we got c++ support working we need to rebuild ncurses for it.

#prep sourcee 
cd /packages
tar -xf ncurses-6.5.tar.gz
cd ncurses-6.5 

mkdir -p build
pushd build
   ../configure --prefix=/usr \
                --without-gpm 
   make -C include
   make -C progs/../include
   make -C progs tic
popd

./configure --prefix=/usr \
    --with-shared \
    --with-normal \
    --without-debug \
    --without-ada \
    --without-tests \
    --disable-stripping \
    --without-gpm \
    --enable-widec \
    --enable-pc-files \
    --with-pkg-config-libdir=/usr/lib/pkgconfig \
    AWK=gawk

make && make install

Vim

Vim is a classic text editor found almost everywhere which is why I included it here. In a future series exploring the capablities of zig I’ll probably swap this out with flow.

#prep sourcee 
cd /packages
tar -xf vim-9.1.1166.tar.gz
cd vim-9.1.1166

./configure --prefix=/usr
make 
make install

Cleanup

This is the end of the temp system. We need to cleanup any archive files and docs. As the next blog post will be rebuilding the whole system inside of a chroot from scratch

rm -rf /usr/share/{info,man,doc}/*
find /usr/{lib,libexec} -name \*.la -delete

################################## EXIT THE CHROOT! ##################################

Assembling the Disk Image

cd $MLFS

# Create a 10GB image to give room to play around
dd if=/dev/zero of=disk.img bs=1M count=10240
sudo mkfs.ext4 disk.img

mkdir -pv mntimg
sudo mount disk.img mntimg

sudo rsync -av --delete $ROOTFS/ mntimg/
sudo mkdir -vp mntimg/boot/extlinux

# Copy Syslinux modules - Arch Linux
sudo cp -vr /usr/lib/syslinux/bios/*.c32 mntimg/boot/extlinux/

# Copy Syslinux modules - Slackware
sudo cp -vr /usr/share/syslinux/*.c32 mntimg/boot/extlinux/

# If unsure, find them
find / -name "*.c32" 2>/dev/null

cat >> extlinux.conf << 'EOF'
DEFAULT linux
LABEL linux
   KERNEL /boot/bzImage
   APPEND root=/dev/sda rw init=/usr/sbin/init console=ttyS0
   TEXT HELP
       Boot the minimal playground kernel with your static rootfs.
   ENDTEXT
EOF

sudo cp -apvr extlinux.conf mntimg/boot/extlinux/extlinux.conf

sudo extlinux -i mntimg/boot/extlinux
sync
sudo umount mntimg

Booting in QEMU

qemu-system-x86_64 -cpu host -enable-kvm -m 4G -drive file=disk.img,format=raw -nographic -serial mon:stdio
bash
source /root/.bashrc

Conclusion

With this we now have a fully working clang inside our chroot. The next step on this journy is to rebuild the entire system against the new compiler! (That includes the compiler) For this will be following the order in the next post of LFS 12.3. (I know 12.4..) The excpetions will be we’ll be using dinit, and not building gcc, gnu-binutils, or glibc as part of the base image. we’ll be using dinit as our init system, which will be covered in part 4

I strongly reconmend making a backup of your rootfs in case of breakages.

I’m sure there are bugs in this, that I’ll hopefully find in time. If you find one let me know and I’ll update the blog post to relect with credit to it’s source.

Sincerely, a concrete worker. May the peace and grace of our Lord be with you.

Musl Based LFS pt. 1