The Funtoo Linux project has transitioned to "Hobby Mode" and this wiki is now read-only.
Difference between revisions of "User:Invakid404/CLFS"
Invakid404 (talk | contribs) m (Wrap the CLFS environment variable in quotes) |
(make requires --without-guile or will otherwise auto-detect guile on host system and include it in the build.) |
||
(66 intermediate revisions by 2 users not shown) | |||
Line 36: | Line 36: | ||
The host variable is straightforward: | The host variable is straightforward: | ||
{{console|body= | {{console|body= | ||
$##i## export CLFS_HOST=$(echo ${MACHTYPE} | $##i## export CLFS_HOST=$(echo ${MACHTYPE} {{!}} sed -e 's/-[^-]*/-cross/') | ||
$##i## echo "${CLFS_HOST}" | $##i## echo "${CLFS_HOST}" | ||
x86_64-cross-linux-gnu | x86_64-cross-linux-gnu | ||
Line 65: | Line 65: | ||
Note that one can put these exports in a file and source them every time one restarts their session to avoid having to setting them manually each and every time. The same applies for all convenience environment variables we'll define later on. | Note that one can put these exports in a file and source them every time one restarts their session to avoid having to setting them manually each and every time. The same applies for all convenience environment variables we'll define later on. | ||
Let's also set our PATH to something more restricted to reduce the influence of the host machine to a minimum. We'll also need to add our cross-tools directory to PATH to be able to use the cross compiler later: | |||
{{console|body= | |||
$##i## export PATH="${CLFS}/cross-tools/bin:/bin:/usr/bin" | |||
}} | |||
=== Building the cross compiler (Chapter 3) === | === Building the cross compiler (Chapter 3) === | ||
Line 86: | Line 92: | ||
{{console|body= | {{console|body= | ||
$##i## cd "${CLFS}"/sources | $##i## cd "${CLFS}"/sources | ||
$##i## wget https://cdn.kernel.org/pub/linux/kernel/v$(echo "${LINUX_VERSION}" | $##i## wget https://cdn.kernel.org/pub/linux/kernel/v$(echo "${LINUX_VERSION}" {{!}} cut -d'.' -f1).x/linux-"${LINUX_VERSION}".tar.xz | ||
$##i## tar xvf linux-"${LINUX_VERSION}".tar.xz | $##i## tar xvf linux-"${LINUX_VERSION}".tar.xz | ||
}} | }} | ||
Line 104: | Line 110: | ||
$##i## wget https://ftp.gnu.org/gnu/binutils/binutils-"${BINUTILS_VERSION}".tar.xz | $##i## wget https://ftp.gnu.org/gnu/binutils/binutils-"${BINUTILS_VERSION}".tar.xz | ||
$##i## tar xvf binutils-"${BINUTILS_VERSION}".tar.xz | $##i## tar xvf binutils-"${BINUTILS_VERSION}".tar.xz | ||
}} | }} | ||
Now, let's configure binutils. The options we'll use are explained [http://clfs.org/view/sysvinit/x86_64-64/cross-tools/binutils.html here]. The only deviation we'll make is we'll prepend the prefix and lib path with the CLFS directory (this will be a reoccurring trend in this page since we aren't creating the symlinks in /) and we'll skip over some flags that aren't 100% necessary: | Now, let's configure binutils. The options we'll use are explained [http://clfs.org/view/sysvinit/x86_64-64/cross-tools/binutils.html here]. The only deviation we'll make is we'll prepend the prefix and lib path with the CLFS directory (this will be a reoccurring trend in this page since we aren't creating the symlinks in /) and we'll skip over some flags that aren't 100% necessary: | ||
{{console|body= | {{console|body= | ||
$##i## mkdir binutils-build | |||
$##i## pushd binutils-build | |||
$##i## AR=ar AS=as ../binutils-${BINUTILS_VERSION}/configure \ | $##i## AR=ar AS=as ../binutils-${BINUTILS_VERSION}/configure \ | ||
--prefix=${CLFS}/cross-tools \ | --prefix=${CLFS}/cross-tools \ | ||
Line 128: | Line 135: | ||
$##i## popd | $##i## popd | ||
}} | }} | ||
==== Static GCC ==== | |||
Now, it's time to build a static GCC, which we'll use to build musl. For this, we'll need to fetch the sources of some GCC dependencies first, namely MPFR, GMP, and MPC: | |||
{{console|body= | |||
$##i## export MPFR_VERSION="4.1.0" | |||
$##i## export GMP_VERSION="6.2.1" | |||
$##i## export MPC_VERSION="1.2.1" | |||
$##i## wget https://ftp.gnu.org/gnu/mpfr/mpfr-"${MPFR_VERSION}".tar.xz | |||
$##i## wget https://ftp.gnu.org/gnu/gmp/gmp-"${GMP_VERSION}".tar.xz | |||
$##i## wget https://ftp.gnu.org/gnu/mpc/mpc-"${MPC_VERSION}".tar.gz | |||
}} | |||
Note that we won't build them separately. Instead, we'll extract them inside the GCC source directory and let the GCC makefile handle that for us. With that out of the way, we should also fetch and extract the GCC sources: | |||
{{console|body= | |||
$##i## export GCC_VERSION="11.2.0" | |||
$##i## wget https://ftp.gnu.org/gnu/gcc/gcc-"${GCC_VERSION}"/gcc-"${GCC_VERSION}".tar.xz | |||
$##i## tar xvf gcc-"${GCC_VERSION}".tar.xz | |||
}} | |||
Now, as mentioned above, we'll move into the GCC source directory and extract its dependencies inside: | |||
{{console|body= | |||
$##i## pushd gcc-"${GCC_VERSION}" | |||
$##i## tar xvf ../mpfr-"${MPFR_VERSION}".tar.xz && mv -v mpfr-"${MPFR_VERSION}" mpfr | |||
$##i## tar xvf ../gmp-"${GMP_VERSION}".tar.xz && mv -v gmp-"${GMP_VERSION}" gmp | |||
$##i## tar xvf ../mpc-"${MPC_VERSION}".tar.gz && mv mpc-"${MPC_VERSION}" mpc | |||
$##i## popd | |||
}} | |||
We can get to building now. Let's create a separate directory for the GCC build and move into it: | |||
{{console|body= | |||
$##i## mkdir gcc-build | |||
$##i## pushd gcc-build | |||
}} | |||
To configure GCC, we'll do a mixture of what's described in the [http://clfs.org/view/sysvinit/x86_64-64/cross-tools/gcc-static.html regular] and [http://clfs.org/view/clfs-embedded/x86/cross-tools/gcc-static.html embedded] CLFS books: | |||
{{console|body= | |||
$##i## AR=ar LDFLAGS="-Wl,-rpath,${CLFS}/cross-tools/lib" \ | |||
../gcc-"${GCC_VERSION}"/configure \ | |||
--prefix=${CLFS}/cross-tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_HOST} \ | |||
--target=${CLFS_TARGET} \ | |||
--with-sysroot=${CLFS} \ | |||
--with-local-prefix=${CLFS}/tools \ | |||
--with-native-system-header-dir=/tools/include \ | |||
--disable-shared \ | |||
--without-headers \ | |||
--with-newlib \ | |||
--disable-decimal-float \ | |||
--disable-libgomp \ | |||
--disable-libssp \ | |||
--disable-libatomic \ | |||
--disable-libitm \ | |||
--disable-libsanitizer \ | |||
--disable-libquadmath \ | |||
--disable-libvtv \ | |||
--disable-libcilkrts \ | |||
--disable-libstdc++-v3 \ | |||
--disable-threads \ | |||
--disable-multilib \ | |||
--enable-languages=c \ | |||
--with-mpfr-include=$(pwd)/../gcc-"${GCC_VERSION}"/mpfr/src \ | |||
--with-mpfr-lib=$(pwd)/mpfr/src/.libs | |||
}} | |||
As mentioned in the book, we don't need to build the entirety of GCC at this stage. The following is enough: | |||
{{console|body= | |||
$##i## make all-gcc all-target-libgcc | |||
$##i## make install-gcc install-target-libgcc | |||
$##i## popd | |||
}} | |||
With that, we should now have a somewhat functioning GCC cross compiler! | |||
{{console|body= | |||
$##i## "${CLFS}"/cross-tools/bin/"${CLFS_TARGET}"-gcc --version | |||
aarch64-unknown-linux-musl-gcc (GCC) 11.2.0 | |||
Copyright (C) 2021 Free Software Foundation, Inc. | |||
This is free software; see the source for copying conditions. There is NO | |||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |||
}} | |||
==== musl ==== | |||
Now that we have a cross compiler, we can use it to build musl. As always, we'll start by fetching and extracting the sources: | |||
{{console|body= | |||
$##i## export MUSL_VERSION="1.2.2" | |||
$##i## wget https://musl.libc.org/releases/musl-"${MUSL_VERSION}".tar.gz | |||
$##i## tar xvf musl-"${MUSL_VERSION}".tar.gz | |||
$##i## pushd musl-"${MUSL_VERSION}" | |||
}} | |||
Configuring musl is pretty straightforward. We'll do it pretty much exactly like in the [http://clfs.org/view/clfs-embedded/x86/cross-tools/libc.html embedded] book. | |||
{{console|body= | |||
$##i## CC="${CLFS_TARGET}-gcc ${CLFS_CFLAGS}" \ | |||
./configure \ | |||
CROSS_COMPILE=${CLFS_TARGET}- \ | |||
--prefix=/ \ | |||
--target=${CLFS_TARGET} | |||
}} | |||
Note that we're also setting the "CC" environment variable with our cross compiler and the CFLAGS we've defined at the start. Compiling and installing musl is just as straightforward: | |||
{{console|body= | |||
$##i## make | |||
$##i## DESTDIR=${CLFS}/tools make install | |||
$##i## popd | |||
}} | |||
We have musl installed now! There's a couple of small issues though... | |||
The first one is subtle and although it won't be apparent for a while, it will bite us later, so let's look into it right now. If we peek into the lib directory, we'll notice that something is off: | |||
{{console|body= | |||
$##i## pushd "${CLFS}"/tools/lib | |||
$##i## ls -la | |||
total 3616 | |||
drwxr-xr-x 2 invakid404 invakid404 270 Feb 23 09:42 . | |||
drwxr-xr-x 4 invakid404 invakid404 32 Feb 23 09:42 .. | |||
-rw-r--r-- 1 invakid404 invakid404 1736 Feb 23 09:42 Scrt1.o | |||
-rw-r--r-- 1 invakid404 invakid404 2000 Feb 23 09:42 crt1.o | |||
-rw-r--r-- 1 invakid404 invakid404 1072 Feb 23 09:42 crti.o | |||
-rw-r--r-- 1 invakid404 invakid404 1016 Feb 23 09:42 crtn.o | |||
lrwxrwxrwx 1 invakid404 invakid404 12 Feb 23 09:42 ld-musl-aarch64.so.1 -> /lib/libc.so | |||
-rw-r--r-- 1 invakid404 invakid404 2709306 Feb 23 09:42 libc.a | |||
-rwxr-xr-x 1 invakid404 invakid404 937488 Feb 23 09:42 libc.so | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libcrypt.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libdl.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libm.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libpthread.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libresolv.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 librt.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libutil.a | |||
-rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libxnet.a | |||
-rw-r--r-- 1 invakid404 invakid404 2648 Feb 23 09:42 rcrt1.o | |||
}} | |||
The dynamic linker symlink is incorrectly pointing at "/lib/libc.so", while it should be pointing at the libc.so in the current directory. Thankfully, this is an easy fix: | |||
{{console|body= | |||
$##i## unlink ld-musl-aarch64.so.1 | |||
$##i## ln -sf libc.so ld-musl-aarch64.so.1 | |||
$##i## ls -la ld-musl-aarch64.so.1 | |||
lrwxrwxrwx 1 invakid404 invakid404 7 Feb 23 09:46 ld-musl-aarch64.so.1 -> libc.so | |||
}} | |||
And now it's fine. Note that we made the symlink relative instead of absolute, that way it'll work when we chroot as well. | |||
The other issue we'll run into in the next step is, as you might have noticed from the command output above, the startfiles (crt*.o) are installed in tools instead of cross-tools. This would result in a linker error while building libgcc, so we'll need to create some symlinks to those in cross-tools to get things to work properly: | |||
{{console|body= | |||
$##i## find -type f \ | |||
-iname 'crt*.o' \ | |||
-exec ln -sf ../../../tools/lib/{} "${CLFS}"/cross-tools/"${CLFS_TARGET}"/lib/{} \; | |||
$##i## ls -la "${CLFS}"/cross-tools/"${CLFS_TARGET}"/lib | |||
total 16 | |||
drwxr-xr-x 3 invakid404 invakid404 65 Feb 23 10:00 . | |||
drwxr-xr-x 4 invakid404 invakid404 28 Feb 23 09:16 .. | |||
lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crt1.o -> ../../../tools/lib/./crt1.o | |||
lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crti.o -> ../../../tools/lib/./crti.o | |||
lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crtn.o -> ../../../tools/lib/./crtn.o | |||
drwxr-xr-x 2 invakid404 invakid404 12288 Feb 23 09:16 ldscripts | |||
$##i## popd | |||
}} | |||
This should be sufficient to get GCC to build properly in the next step. | |||
==== Final GCC ==== | |||
Having done that, we're now ready to build GCC against our freshly built musl. We already have everything fetched and set up correctly, so let's just wipe the "gcc-build" directory clean and start over: | |||
{{console|body= | |||
$##i## pushd gcc-build | |||
$##i## rm -r * | |||
}} | |||
We can now re-enable the long list of libraries we disabled when we built the static GCC, with a single exception: we'll need to keep libsanitizer disabled, since it doesn't seem to build correctly with musl due to the fact musl has no "fstab.h" header. Again, we'll use a mixture of what the [http://clfs.org/view/clfs-embedded/x86/cross-tools/gcc-final.html embedded] and [http://clfs.org/view/sysvinit/x86_64-64/cross-tools/gcc-final.html regular] CLFS books list as flags: | |||
{{console|body= | |||
$##i## AR=ar LDFLAGS="-Wl,-rpath,${CLFS}/cross-tools/lib" \ | |||
../gcc-"${GCC_VERSION}"/configure \ | |||
--prefix=${CLFS}/cross-tools \ | |||
--build=${CLFS_HOST} \ | |||
--target=${CLFS_TARGET} \ | |||
--host=${CLFS_HOST} \ | |||
--with-sysroot=${CLFS} \ | |||
--with-local-prefix=${CLFS}/tools \ | |||
--with-native-system-header-dir=/tools/include \ | |||
--with-lib-path=${CLFS}/tools/lib \ | |||
--disable-nls \ | |||
--disable-static \ | |||
--enable-languages=c,c++ \ | |||
--disable-multilib \ | |||
--disable-libsanitizer \ | |||
--with-mpfr-include=$(pwd)/../gcc-"${GCC_VERSION}"/mpfr/src \ | |||
--with-mpfr-lib=$(pwd)/mpfr/src/.libs | |||
}} | |||
Unlike the static GCC build, we have to build the entirety of GCC now: | |||
{{console|body= | |||
$##i## make AS_FOR_TARGET="${CLFS_TARGET}-as" \ | |||
LD_FOR_TARGET="${CLFS_TARGET}-ld" | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
=== Building the basic tools (Chapter 4.6) === | |||
Now, we're ready to start getting some tools cross-compiled for our target architecture. For the sake of completeness, we'll install everything listed under "Constructing a Temporary System" in the CLFS book, even if not everything we build is strictly necessary later. | |||
==== Using the cross compiler ==== | |||
To use the cross compiler, we'll need to set some environment variables. I personally find it nice to spawn a new instance of "bash" to scope those variables to be able to get rid of them later: | |||
{{console|body= | |||
$##i## env \ | |||
TERM="${TERM}" \ | |||
CC="${CLFS_TARGET}-gcc ${CLFS_CFLAGS}" \ | |||
CXX="${CLFS_TARGET}-g++ ${CLFS_CFLAGS}" \ | |||
AR="${CLFS_TARGET}-ar" \ | |||
AS="${CLFS_TARGET}-as" \ | |||
RANLIB="${CLFS_TARGET}-ranlib" \ | |||
LD="${CLFS_TARGET}-ld" \ | |||
STRIP="${CLFS_TARGET}-strip" \ | |||
DYNAMIC_LINKER=$(eval echo "${CLFS}/tools/lib/ld-musl-*.so.1") \ | |||
bash --rcfile <(cat ~/.bashrc 2>/dev/null; echo 'PS1="(cross) ${PS1}"') | |||
$##i## echo "${CC}" | |||
aarch64-unknown-linux-musl-gcc | |||
}} | |||
The only non-standard variable in the list above is DYNAMIC_LINKER, which we'll need at the end of preparing the basic tools. | |||
==== GMP ==== | |||
Unlike when building the cross compiler, we'll build and install GMP separately instead. We already have the GMP sources, so we'll just do a clean extract and get to building. The only thing we'll be consistently changing in the configure commands is the prefix, because we don't have a /tools symlink: | |||
{{console|body= | |||
$##i## tar xvf gmp-"${GMP_VERSION}".tar.xz | |||
$##i## pushd gmp-"${GMP_VERSION}" | |||
$##i## CC_FOR_BUILD=gcc \ | |||
./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--enable-cxx | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== MPFR ==== | |||
Pretty much the same as above: | |||
{{console|body= | |||
$##i## tar xvf mpfr-"${MPFR_VERSION}".tar.xz | |||
$##i## pushd mpfr-"${MPFR_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== MPC ==== | |||
Same as above: | |||
{{console|body= | |||
$##i## tar xvf mpc-"${MPC_VERSION}".tar.gz | |||
$##i## pushd mpc-"${MPC_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== ISL ==== | |||
While this is an optional dependency and we skipped it earlier, we'll build it now, because why not :D | |||
Nothing special, just fetch and unpack the sources, configure, build and install: | |||
{{console|body= | |||
$##i## export ISL_VERSION="0.24" | |||
$##i## wget https://gcc.gnu.org/pub/gcc/infrastructure/isl-"${ISL_VERSION}".tar.bz2 | |||
$##i## tar xvf isl-"${ISL_VERSION}".tar.bz2 | |||
$##i## pushd isl-"${ISL_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Zlib ==== | |||
Nothing unusual yet again: | |||
{{console|body= | |||
$##i## export ZLIB_VERSION="1.2.11" | |||
$##i## wget https://zlib.net/zlib-"${ZLIB_VERSION}".tar.gz | |||
$##i## tar xvf zlib-"${ZLIB_VERSION}".tar.gz | |||
$##i## pushd zlib-"${ZLIB_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Binutils ==== | |||
No special steps here either: | |||
{{console|body= | |||
$##i## pushd binutils-build | |||
$##i## rm -rf * | |||
$##i## ../binutils-${BINUTILS_VERSION}/configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--target=${CLFS_TARGET} \ | |||
--with-lib-path=${CLFS}/tools/lib \ | |||
--disable-nls \ | |||
--enable-shared \ | |||
--enable-64-bit-bfd \ | |||
--disable-multilib \ | |||
--enable-gold=yes \ | |||
--enable-plugins \ | |||
--with-system-zlib \ | |||
--enable-threads | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== GCC ==== | |||
This is where we'll need to do some patching to get things to work. We'll need to disable stdinc++ (refer to [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100017 this bug]): | |||
{{console|body= | |||
$##i## pushd gcc-"${GCC_VERSION}" | |||
$##i## sed -i \ | |||
's{{!}}^RAW_CXX_FOR_TARGET="$CXX_FOR_TARGET{{!}}& -nostdinc++{{!}}' \ | |||
configure | |||
$##i## popd | |||
}} | |||
We'll also need to disable libstdcxx-pch and libsanitizer for similar reasons as before: | |||
{{console|body= | |||
$##i## pushd gcc-build | |||
$##i## rm -r * | |||
$##i## ../gcc-"${GCC_VERSION}"/configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--target=${CLFS_TARGET} \ | |||
--with-local-prefix=${CLFS}/tools \ | |||
--disable-multilib \ | |||
--enable-languages=c,c++ \ | |||
--with-system-zlib \ | |||
--with-native-system-header-dir=/tools/include \ | |||
--disable-libssp \ | |||
--enable-install-libiberty \ | |||
--disable-libstdcxx-pch \ | |||
--disable-libsanitizer | |||
$##i## make AS_FOR_TARGET="${AS}" \ | |||
LD_FOR_TARGET="${LD}" | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Ncurses ==== | |||
We'll deviate from the CLFS book here as well. Let's fetch the sources first: | |||
{{console|body= | |||
$##i## export NCURSES_VERSION="6.3" | |||
$##i## wget https://ftp.gnu.org/gnu/ncurses/ncurses-"${NCURSES_VERSION}".tar.gz | |||
$##i## tar xvf ncurses-"${NCURSES_VERSION}".tar.gz | |||
$##i## pushd ncurses-"${NCURSES_VERSION}" | |||
}} | |||
First things first, we need to instruct ncurses to use the cross-compiled strip instead of the host one: | |||
{{console|body= | |||
$##i## sed -i -e "s/INSTALL_OPT_S=\\\"-s/& --strip-program=${STRIP}/" configure | |||
}} | |||
Then, we'll need to build both ncurses and ncursesw since things will fail down the line if we don't. Let's build the regular one first. Note that we're also enabling terminfo-related flags to ensure things like clearing the terminal are going to work when we chroot: | |||
{{console|body= | |||
$##i## mkdir ncurses-build | |||
$##i## pushd ncurses-build | |||
$##i## ../configure \ | |||
--prefix=${CLFS}/tools \ | |||
--with-shared \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--without-debug \ | |||
--without-ada \ | |||
--enable-overwrite \ | |||
--with-build-cc=gcc \ | |||
--enable-overwrite \ | |||
--with-default-terminfo-dir=${CLFS}/tools/share/terminfo \ | |||
--with-termlib | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
Similarly, we'll build ncursesw as well. Note that we're changing the includedir as well, as that's where other packages are going to expect the headers to be: | |||
{{console|body= | |||
$##i## mkdir ncursesw-build | |||
$##i## pushd ncursesw-build | |||
$##i## ../configure \ | |||
--prefix=${CLFS}/tools \ | |||
--with-shared \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--without-debug \ | |||
--without-ada \ | |||
--enable-overwrite \ | |||
--with-build-cc=gcc \ | |||
--enable-widec \ | |||
--includedir=${CLFS}/tools/include/ncursesw \ | |||
--with-default-terminfo-dir=${CLFS}/tools/share/terminfo \ | |||
--with-termlib | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
$##i## popd | |||
}} | |||
==== Bash ==== | |||
Nothing too interesting here either. | |||
{{console|body= | |||
$##i## export BASH_VERSION="5.1.16" | |||
$##i## wget https://ftp.gnu.org/gnu/bash/bash-"${BASH_VERSION}".tar.gz | |||
$##i## tar xvf bash-"${BASH_VERSION}".tar.gz | |||
$##i## pushd bash-"${BASH_VERSION}" | |||
$##i## cat > config.cache << "EOF" | |||
ac_cv_func_mmap_fixed_mapped=yes | |||
ac_cv_func_strcoll_works=yes | |||
ac_cv_func_working_mktime=yes | |||
bash_cv_func_sigsetjmp=present | |||
bash_cv_getcwd_malloc=yes | |||
bash_cv_job_control_missing=present | |||
bash_cv_printf_a_format=yes | |||
bash_cv_sys_named_pipes=present | |||
bash_cv_ulimit_maxfds=yes | |||
bash_cv_under_sys_siglist=yes | |||
bash_cv_unusable_rtsigs=no | |||
gt_cv_int_divbyzero_sigfpe=yes | |||
EOF | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--without-bash-malloc \ | |||
--cache-file=config.cache | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Bzip2 ==== | |||
Here we'll have to enable position-independent code (-fPIC), otherwise "file" won't link properly. Other than that, standard stuff: | |||
{{console|body= | |||
$##i## export BZIP2_VERSION="1.0.8" | |||
$##i## wget https://www.sourceware.org/pub/bzip2/bzip2-"${BZIP2_VERSION}".tar.gz | |||
$##i## tar xvf bzip2-"${BZIP2_VERSION}".tar.gz | |||
$##i## pushd bzip2-"${BZIP2_VERSION}" | |||
$##i## sed -i \ | |||
-e '/^all/s/ test$//' \ | |||
-e 's{{!}}CFLAGS={{!}}&-fPIC {{!}}' \ | |||
Makefile | |||
$##i## make CC="${CC}" AR="${AR}" RANLIB="${RANLIB}" | |||
$##i## make PREFIX=${CLFS}/tools install | |||
$##i## popd | |||
}} | |||
==== Check ==== | |||
While check definitely isn't necessary, we'll install it anyway: | |||
{{console|body= | |||
$##i## export CHECK_VERSION="0.15.2" | |||
$##i## wget https://github.com/libcheck/check/releases/download/"${CHECK_VERSION}"/check-"${CHECK_VERSION}".tar.gz | |||
$##i## tar xvf check-"${CHECK_VERSION}".tar.gz | |||
$##i## pushd check-"${CHECK_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--disable-subunit | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Coreutils ==== | |||
Nothing unusual: | |||
{{console|body= | |||
$##i## export COREUTILS_VERSION="9.0" | |||
$##i## wget https://ftp.gnu.org/gnu/coreutils/coreutils-"${COREUTILS_VERSION}".tar.xz | |||
$##i## tar xvf coreutils-"${COREUTILS_VERSION}".tar.xz | |||
$##i## pushd coreutils-"${COREUTILS_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--enable-install-program=hostname \ | |||
--cache-file=config.cache | |||
$##i## sed -i -e 's/^man1_MANS/#man1_MANS/' Makefile | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Diffutils ==== | |||
Same here: | |||
{{console|body= | |||
$##i## export DIFFUTILS_VERSION="3.8" | |||
$##i## wget https://ftp.gnu.org/gnu/diffutils/diffutils-"${DIFFUTILS_VERSION}".tar.xz | |||
$##i## tar xvf diffutils-"${DIFFUTILS_VERSION}".tar.xz | |||
$##i## pushd diffutils-"${DIFFUTILS_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== File ==== | |||
We'll enable position-independent code explicitly here just in case, although it's enabled by default: | |||
{{console|body= | |||
$##i## export FILE_VERSION="5.41" | |||
$##i## wget http://ftp.astron.com/pub/file/file-"${FILE_VERSION}".tar.gz | |||
$##i## tar xvf file-"${FILE_VERSION}".tar.gz | |||
$##i## pushd file-"${FILE_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--with-pic=yes | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Findutils ==== | |||
The usual business here: | |||
{{console|body= | |||
$##i## export FINDUTILS_VERSION="4.9.0" | |||
$##i## wget https://ftp.gnu.org/gnu/findutils/findutils-"${FINDUTILS_VERSION}".tar.xz | |||
$##i## tar xvf findutils-"${FINDUTILS_VERSION}".tar.xz | |||
$##i## pushd findutils-"${FINDUTILS_VERSION}" | |||
$##i## echo "gl_cv_func_wcwidth_works=yes" > config.cache | |||
$##i## echo "ac_cv_func_fnmatch_gnu=yes" >> config.cache | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--cache-file=config.cache | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Gawk ==== | |||
Standard procedure here: | |||
{{console|body= | |||
$##i## export GAWK_VERSION="5.1.1" | |||
$##i## wget https://ftp.gnu.org/gnu/gawk/gawk-"${GAWK_VERSION}".tar.xz | |||
$##i## tar xvf gawk-"${GAWK_VERSION}".tar.xz | |||
$##i## pushd gawk-"${GAWK_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Gettext ==== | |||
We'll install the entirety of gettext for simplicity: | |||
{{console|body= | |||
$##i## export GETTEXT_VERSION="0.21" | |||
$##i## wget https://ftp.gnu.org/gnu/gettext/gettext-"${GETTEXT_VERSION}".tar.xz | |||
$##i## tar xvf gettext-"${GETTEXT_VERSION}".tar.xz | |||
$##i## pushd gettext-"${GETTEXT_VERSION}" | |||
$##i## EMACS="no" \ | |||
./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--disable-shared | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Grep ==== | |||
Nothing special: | |||
{{console|body= | |||
$##i## export GREP_VERSION="3.7" | |||
$##i## wget https://ftp.gnu.org/gnu/grep/grep-"${GREP_VERSION}".tar.xz | |||
$##i## tar xvf grep-"${GREP_VERSION}".tar.xz | |||
$##i## pushd grep-"${GREP_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Gzip ==== | |||
The usual business: | |||
{{console|body= | |||
$##i## export GZIP_VERSION="1.11" | |||
$##i## wget https://ftp.gnu.org/gnu/gzip/gzip-"${GZIP_VERSION}".tar.gz | |||
$##i## tar xvf gzip-"${GZIP_VERSION}".tar.gz | |||
$##i## pushd gzip-"${GZIP_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Make ==== | |||
No differences here: | |||
{{console|body= | |||
$##i## export MAKE_VERSION="4.3" | |||
$##i## wget https://ftp.gnu.org/gnu/make/make-"${MAKE_VERSION}".tar.gz | |||
$##i## tar xvf make-"${MAKE_VERSION}".tar.gz | |||
$##i## pushd make-"${MAKE_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--without-guile | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Patch ==== | |||
The usual stuff: | |||
{{console|body= | |||
$##i## export PATCH_VERSION="2.7.6" | |||
$##i## wget https://ftp.gnu.org/gnu/patch/patch-"${PATCH_VERSION}".tar.xz | |||
$##i## tar xvf patch-"${PATCH_VERSION}".tar.xz | |||
$##i## pushd patch-"${PATCH_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Sed ==== | |||
You've probably picked the pattern up already, nothing special here either: | |||
{{console|body= | |||
$##i## export SED_VERSION="4.8" | |||
$##i## wget https://ftp.gnu.org/gnu/sed/sed-"${SED_VERSION}".tar.xz | |||
$##i## tar xvf sed-"${SED_VERSION}".tar.xz | |||
$##i## pushd sed-"${SED_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Tar ==== | |||
Same here: | |||
{{console|body= | |||
$##i## export TAR_VERSION="1.34" | |||
$##i## wget https://ftp.gnu.org/gnu/tar/tar-"${TAR_VERSION}".tar.xz | |||
$##i## tar xvf tar-"${TAR_VERSION}".tar.xz | |||
$##i## pushd tar-"${TAR_VERSION}" | |||
$##i## cat > config.cache << EOF | |||
gl_cv_func_wcwidth_works=yes | |||
gl_cv_func_btowc_eof=yes | |||
ac_cv_func_malloc_0_nonnull=yes | |||
gl_cv_func_mbrtowc_incomplete_state=yes | |||
gl_cv_func_mbrtowc_nul_retval=yes | |||
gl_cv_func_mbrtowc_null_arg1=yes | |||
gl_cv_func_mbrtowc_null_arg2=yes | |||
gl_cv_func_mbrtowc_retval=yes | |||
gl_cv_func_wcrtomb_retval=yes | |||
EOF | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--cache-file=config.cache | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Texinfo ==== | |||
You've guessed it: | |||
{{console|body= | |||
$##i## export TEXINFO_VERSION="6.8" | |||
$##i## wget https://ftp.gnu.org/gnu/texinfo/texinfo-"${TEXINFO_VERSION}".tar.xz | |||
$##i## tar xvf texinfo-"${TEXINFO_VERSION}".tar.xz | |||
$##i## pushd texinfo-"${TEXINFO_VERSION}" | |||
$##i## PERL=/usr/bin/perl \ | |||
./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Util-linux ==== | |||
Just as I was running out of things to say, we finally got to a point where we need to do some patching. For some reason, util-linux doesn't pick up the tinfow library automatically, so we need to add it to the LDFLAGS manually to avoid a "DSO missing from command line error": | |||
{{console|body= | |||
$##i## export UTIL_LINUX_VERSION="2.37.4" | |||
$##i## wget https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v$(echo ${UTIL_LINUX_VERSION} {{!}} cut -d'.' -f1-2)/util-linux-"${UTIL_LINUX_VERSION}".tar.gz | |||
$##i## tar xvf util-linux-"${UTIL_LINUX_VERSION}".tar.gz | |||
$##i## pushd util-linux-"${UTIL_LINUX_VERSION}" | |||
$##i## NCURSESW6_CONFIG=" " \ | |||
NCURSES6_CONFIG=" " \ | |||
NCURSESW5_CONFIG=" " \ | |||
NCURSES5_CONFIG=" " \ | |||
LDFLAGS="-ltinfow" \ | |||
./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} \ | |||
--disable-bash-completion \ | |||
--disable-makeinstall-chown \ | |||
--disable-makeinstall-setuid \ | |||
--disable-nologin \ | |||
--without-python | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Nano ==== | |||
Nano suffers from the same issue as util-linux. Thankfully, we know how to fix it: | |||
{{console|body= | |||
$##i## export NANO_VERSION="6.1" | |||
$##i## wget https://ftp.gnu.org/gnu/nano/nano-"${NANO_VERSION}".tar.xz | |||
$##i## tar xvf nano-"${NANO_VERSION}".tar.xz | |||
$##i## pushd nano-"${NANO_VERSION}" | |||
$##i## LDFLAGS="-ltinfow" \ | |||
./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== xz-utils ==== | |||
Last, but not least, xz-utils requires no special attention to build: | |||
{{console|body= | |||
$##i## export XZ_UTILS_VERSION="5.2.5" | |||
$##i## wget https://tukaani.org/xz/xz-"${XZ_UTILS_VERSION}".tar.gz | |||
$##i## tar xvf xz-"${XZ_UTILS_VERSION}".tar.gz | |||
$##i## pushd xz-"${XZ_UTILS_VERSION}" | |||
$##i## ./configure \ | |||
--prefix=${CLFS}/tools \ | |||
--build=${CLFS_HOST} \ | |||
--host=${CLFS_TARGET} | |||
$##i## make | |||
$##i## make install | |||
$##i## popd | |||
}} | |||
==== Finishing touches ==== | |||
With all of that done, we're almost ready to chroot. There are two things to fix at this point: | |||
* All ELF binaries expect the dynamic linker and runtime dependencies to be in / instead of /tools. We'll need to patch those if we want them to work. | |||
* All pkgconfig files have a prefix that contains the absolute path to our CLFS directory. For those to work after chrooting, we'll need to strip it. | |||
To address the first issue, this command utilizes the patchelf utility to set the dynamic linker (interpreter) and runtime dependency path (rpath): | |||
{{console|body= | |||
$##i## find "${CLFS}"/tools -type f -exec file {} + {{!}} \ | |||
grep ELF {{!}} \ | |||
cut -d':' -f1 {{!}} \ | |||
xargs -I{} patchelf \ | |||
--set-interpreter "${DYNAMIC_LINKER#$CLFS}" \ | |||
--set-rpath /tools/lib/ {} 2>/dev/null | |||
}} | |||
We can verify that it works by checking some binary such as bash: | |||
{{console|body= | |||
$##i## file "${CLFS}"/tools/bin/bash | |||
/home/invakid404/clfs/tools/bin/bash: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /tools/lib/ld-musl-aarch64.so.1, with debug_info, not stripped | |||
}} | |||
As you can see, the interpreter is now prefixed with /tools, as it should be. | |||
To address the second issue, we can combine find and sed and strip all occurrences of the prefix from all pkgtools files: | |||
{{console|body= | |||
$##i## find ${CLFS}/tools -iname '*.pc' \ | |||
-exec sed -i -e "s{{!}}${CLFS}{{!}}{{!}}g" {} \; | |||
}} | |||
=== Chrooting (Chapter 4.8) === | |||
We should be all prepared for chrooting now. Let's start by fleshing out some directories and creating some devices as per [http://clfs.org/view/sysvinit/x86_64-64/chroot/kernfs.html the book]: | |||
{{Note|In the command below, depending on how you 'switch to root', you may no longer have the {{c|<nowiki>${CLFS}</nowiki>}} variable set! This may require you to manually specify the path for the {{c|mknod}} commands.}} | |||
{{console|body= | |||
$##i## mkdir -pv ${CLFS}/{dev,proc,run,sys} | |||
mkdir: created directory '/home/invakid404/clfs/dev' | |||
mkdir: created directory '/home/invakid404/clfs/proc' | |||
mkdir: created directory '/home/invakid404/clfs/run' | |||
mkdir: created directory '/home/invakid404/clfs/sys' | |||
###i## mknod -m 600 ${CLFS}/dev/console c 5 1 | |||
###i## mknod -m 666 ${CLFS}/dev/null c 1 3 | |||
}} | |||
We don't need to create the bind mounts manually, since fchroot does that for us. Speaking of fchroot, it's time to utilize it! | |||
{{console|body= | |||
###i## sudo fchroot . /tools/bin/bash | |||
Funtoo fchroot 0.2 ("Darth Elmo"); Copyright 2020-2022 Funtoo Solutions, Inc. | |||
Licensed under the Apache License, Version 2.0 | |||
>>> Entering arm-64bit (cortex-a53 CPU) fchroot... | |||
%bash-5.1% | |||
}} | |||
And just like that, we've got a prompt! We won't be able to run anything yet though since /tools isn't in our path. Let's fix that: | |||
{{Note|Maybe I built in the wrong directory, but I had many more things in the chroot, due to all the source directories still existing there! -DR}} | |||
{{console|body= | |||
%bash-5.1% export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin | |||
%bash-5.1% ls | |||
cross-tools dev etc proc run sources sys tools usr | |||
}} | |||
Now we're talking! We'll need to export some more stuff, such as the terminal and terminfo location. I'll set "xterm" for my terminal since I use kitty and the kitty terminfo isn't installed: | |||
{{console|body= | |||
%bash-5.1% export TERM="xterm" | |||
%bash-5.1% export TERMINFO="/tools/share/terminfo" | |||
}} | |||
Now things like nano should work. Let's try to write a "hello world" program in C to verify that things are working: | |||
{{console|body= | |||
%bash-5.1% nano hello.c | |||
#include <stdio.h> | |||
int main() { | |||
puts("Hello, world!"); | |||
return 0; | |||
} | |||
%bash-5.1% gcc -o hello hello.c | |||
Error loading shared library libstdc++.so.6: No such file or directory (needed by /tools/bin/gcc) | |||
Error loading shared library libgcc_s.so.1: No such file or directory (needed by /tools/bin/gcc) | |||
Error relocating /tools/bin/gcc: _ZSt20__throw_length_errorPKc: symbol not found | |||
Error relocating /tools/bin/gcc: _ZdaPvm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZSt24__throw_out_of_range_fmtPKcz: symbol not found | |||
Error relocating /tools/bin/gcc: __cxa_guard_acquire: symbol not found | |||
Error relocating /tools/bin/gcc: _Znam: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZdlPvm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_assignERKS4_: symbol not found | |||
Error relocating /tools/bin/gcc: __cxa_guard_release: symbol not found | |||
Error relocating /tools/bin/gcc: _Unwind_Backtrace: symbol not found | |||
Error relocating /tools/bin/gcc: _ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEcm: symbol not found | |||
Error relocating /tools/bin/gcc: _Unwind_GetIPInfo: symbol not found | |||
Error relocating /tools/bin/gcc: _ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_: symbol not found | |||
Error relocating /tools/bin/gcc: _ZSt19__throw_logic_errorPKc: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE13find_first_ofEPKcmm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZdaPv: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEPKcmm: symbol not found | |||
Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm: symbol not found | |||
Error relocating /tools/bin/gcc: _Znwm: symbol not found | |||
}} | |||
Oh, well... This is fixable though: | |||
{{console|body= | |||
%bash-5.1% pushd /tools | |||
%bash-5.1% mv lib64/* lib/ | |||
%bash-5.1% rmdir lib64 | |||
%bash-5.1% ln -sf lib lib64 | |||
%bash-5.1% popd | |||
}} | |||
For some reason, some things were installed in /tools/lib, and others in /tools/lib64. To patch this, we moved everything into /tools/lib and linked them together. | |||
{{Note|We wouldn't have had this issue if we linked lib and lib64 in the first place, which is also a valid solution (this is how it's done on standard Funtoo systems). Might be worth looking into getting rid of this issue as a whole at some point in the future.}} | |||
Let's try again now! | |||
{{console|body= | |||
%bash-5.1% gcc -o hello hello.c | |||
}} | |||
It builds! Let's try running it: | |||
{{console|body= | |||
%bash-5.1% ./hello | |||
qemu-arm-64bit-wrapper: Could not open '/lib/ld-musl-aarch64.so.1': No such file or directory | |||
}} | |||
Dynamic linker strikes again... Nothing that a symlink can't fix though: | |||
{{console|body= | |||
%bash-5.1% mkdir lib | |||
%bash-5.1% ln -sf /tools/lib/ld-musl-aarch64.so.1 lib/ | |||
}} | |||
Let's try yet again: | |||
{{console|body= | |||
%bash-5.1% ./hello | |||
Hello, world! | |||
}} | |||
And it works! | |||
=== What's next? === | |||
The next steps after this would be to try and get Python running, then Portage, then to kick off a build for a stage3. |
Latest revision as of 22:41, March 29, 2022
CLFS with musl
This page covers the steps of getting a working CLFS "temporary environment" with musl instead of glibc and latest everything.
Setting up the environment (Chapter 2)
Instead of creating a partition, we'll build everything in a directory in our home. Let's assign it to a variable called `CLFS` and create it:
user $ export CLFS="$HOME/clfs" user $ mkdir -v "${CLFS}" mkdir: created directory '/home/invakid404/clfs'
We'll also need to create three subdirectories in our CLFS directory:
- cross-tools: this is where the cross compiler will go
- tools: this is where the so-called "temporary system" will go
- sources: this is where we'll download and build all packages
user $ mkdir -v "${CLFS}"/cross-tools mkdir: created directory '/home/invakid404/clfs/cross-tools' user $ mkdir -v "${CLFS}"/tools mkdir: created directory '/home/invakid404/clfs/tools' user $ mkdir -v "${CLFS}"/sources mkdir: created directory '/home/invakid404/clfs/sources'
Let's also define some build-related environment variables:
- CLFS_HOST: the target triple of the host machine; modified to contain "cross" to handle the case where we're cross-compiling for the same target
- CLFS_TARGET: the target triple of the target architecture
- CLFS_ARCH: the name of the target architecture; used specifically when installing Linux kernel headers
- CLFS_CFLAGS: extra flags we'll pass to the GCC cross compiler
- MAKEFLAGS: flags we want to pass to GNU make by default
The host variable is straightforward:
user $ export CLFS_HOST=$(echo ${MACHTYPE} | sed -e 's/-[^-]*/-cross/') user $ echo "${CLFS_HOST}" x86_64-cross-linux-gnu
The target, arch, and CFLAGS depend on the target architecture you're going for. Here's a table of all currently tested architectures and their respective build variables:
Architecture | CLFS_TARGET | CLFS_ARCH | CLFS_CFLAGS |
---|---|---|---|
x86_64 | x86_64-unknown-linux-musl | x86_64 | -m64 |
aarch64 | aarch64-unknown-linux-musl | arm64 |
The rest of this page will assume aarch64, but the steps should be analogous for all listed architectures:
user $ export CLFS_TARGET="aarch64-unknown-linux-musl" user $ export CLFS_ARCH="arm64" user $ export CLFS_CFLAGS=""
For the makeflags, we'll tell GNU make to run as many jobs in parallel as we have threads to speed up the compilation:
user $ export MAKEFLAGS="-j$(nproc) -l$(nproc)"
Note that one can put these exports in a file and source them every time one restarts their session to avoid having to setting them manually each and every time. The same applies for all convenience environment variables we'll define later on.
Let's also set our PATH to something more restricted to reduce the influence of the host machine to a minimum. We'll also need to add our cross-tools directory to PATH to be able to use the cross compiler later:
user $ export PATH="${CLFS}/cross-tools/bin:/bin:/usr/bin"
Building the cross compiler (Chapter 3)
To get a cross compiler going, we'll need to do the following steps:
- Install the Linux headers for the target architecture
- Build binutils for the target architecture
- Build a static version of GCC without a standard library
- Build musl with the static version of GCC
- Build GCC again, now with musl as the standard library
Note that the official CLFS guide suggests building things like GMP, MPFR, and MPC separately, but we'll extract those in the GCC source directory and leave it up to the GCC makefile to do the heavy-lifting for us for simplicity. We'll also skip over ISL since it's not required and not too interesting for us at this stage.
Linux headers
First things first, let's fetch the Linux kernel sources. Throughout the rest of this page, we'll be putting the versions of packages in environment variables for convenience like so:
user $ export LINUX_VERSION="5.16.10"
Let's cd into the sources directory and download Linux:
user $ cd "${CLFS}"/sources user $ wget https://cdn.kernel.org/pub/linux/kernel/v$(echo "${LINUX_VERSION}" | cut -d'.' -f1).x/linux-"${LINUX_VERSION}".tar.xz user $ tar xvf linux-"${LINUX_VERSION}".tar.xz
Now, let's install the Linux headers for our target architecture. We'll install those in the tools directory to use in the temporary environment as well:
user $ pushd linux-"${LINUX_VERSION}" user $ make mrproper user $ make ARCH=${CLFS_ARCH} INSTALL_HDR_PATH=${CLFS}/tools headers_install user $ popd
Binutils
Let's fetch and extract the binutils sources:
user $ export BINUTILS_VERSION="2.38" user $ wget https://ftp.gnu.org/gnu/binutils/binutils-"${BINUTILS_VERSION}".tar.xz user $ tar xvf binutils-"${BINUTILS_VERSION}".tar.xz
Now, let's configure binutils. The options we'll use are explained here. The only deviation we'll make is we'll prepend the prefix and lib path with the CLFS directory (this will be a reoccurring trend in this page since we aren't creating the symlinks in /) and we'll skip over some flags that aren't 100% necessary:
user $ mkdir binutils-build user $ pushd binutils-build user $ AR=ar AS=as ../binutils-${BINUTILS_VERSION}/configure \ --prefix=${CLFS}/cross-tools \ --host=${CLFS_HOST} \ --target=${CLFS_TARGET} \ --with-sysroot=${CLFS} \ --with-lib-path=${CLFS}/tools/lib \ --disable-nls \ --disable-static \ --enable-64-bit-bfd \ --disable-multilib \ --disable-werror
We're now ready to build and install it:
user $ make user $ make install user $ popd
Static GCC
Now, it's time to build a static GCC, which we'll use to build musl. For this, we'll need to fetch the sources of some GCC dependencies first, namely MPFR, GMP, and MPC:
user $ export MPFR_VERSION="4.1.0" user $ export GMP_VERSION="6.2.1" user $ export MPC_VERSION="1.2.1" user $ wget https://ftp.gnu.org/gnu/mpfr/mpfr-"${MPFR_VERSION}".tar.xz user $ wget https://ftp.gnu.org/gnu/gmp/gmp-"${GMP_VERSION}".tar.xz user $ wget https://ftp.gnu.org/gnu/mpc/mpc-"${MPC_VERSION}".tar.gz
Note that we won't build them separately. Instead, we'll extract them inside the GCC source directory and let the GCC makefile handle that for us. With that out of the way, we should also fetch and extract the GCC sources:
user $ export GCC_VERSION="11.2.0" user $ wget https://ftp.gnu.org/gnu/gcc/gcc-"${GCC_VERSION}"/gcc-"${GCC_VERSION}".tar.xz user $ tar xvf gcc-"${GCC_VERSION}".tar.xz
Now, as mentioned above, we'll move into the GCC source directory and extract its dependencies inside:
user $ pushd gcc-"${GCC_VERSION}" user $ tar xvf ../mpfr-"${MPFR_VERSION}".tar.xz && mv -v mpfr-"${MPFR_VERSION}" mpfr user $ tar xvf ../gmp-"${GMP_VERSION}".tar.xz && mv -v gmp-"${GMP_VERSION}" gmp user $ tar xvf ../mpc-"${MPC_VERSION}".tar.gz && mv mpc-"${MPC_VERSION}" mpc user $ popd
We can get to building now. Let's create a separate directory for the GCC build and move into it:
user $ mkdir gcc-build user $ pushd gcc-build
To configure GCC, we'll do a mixture of what's described in the regular and embedded CLFS books:
user $ AR=ar LDFLAGS="-Wl,-rpath,${CLFS}/cross-tools/lib" \ ../gcc-"${GCC_VERSION}"/configure \ --prefix=${CLFS}/cross-tools \ --build=${CLFS_HOST} \ --host=${CLFS_HOST} \ --target=${CLFS_TARGET} \ --with-sysroot=${CLFS} \ --with-local-prefix=${CLFS}/tools \ --with-native-system-header-dir=/tools/include \ --disable-shared \ --without-headers \ --with-newlib \ --disable-decimal-float \ --disable-libgomp \ --disable-libssp \ --disable-libatomic \ --disable-libitm \ --disable-libsanitizer \ --disable-libquadmath \ --disable-libvtv \ --disable-libcilkrts \ --disable-libstdc++-v3 \ --disable-threads \ --disable-multilib \ --enable-languages=c \ --with-mpfr-include=$(pwd)/../gcc-"${GCC_VERSION}"/mpfr/src \ --with-mpfr-lib=$(pwd)/mpfr/src/.libs
As mentioned in the book, we don't need to build the entirety of GCC at this stage. The following is enough:
user $ make all-gcc all-target-libgcc user $ make install-gcc install-target-libgcc user $ popd
With that, we should now have a somewhat functioning GCC cross compiler!
user $ "${CLFS}"/cross-tools/bin/"${CLFS_TARGET}"-gcc --version aarch64-unknown-linux-musl-gcc (GCC) 11.2.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
musl
Now that we have a cross compiler, we can use it to build musl. As always, we'll start by fetching and extracting the sources:
user $ export MUSL_VERSION="1.2.2" user $ wget https://musl.libc.org/releases/musl-"${MUSL_VERSION}".tar.gz user $ tar xvf musl-"${MUSL_VERSION}".tar.gz user $ pushd musl-"${MUSL_VERSION}"
Configuring musl is pretty straightforward. We'll do it pretty much exactly like in the embedded book.
user $ CC="${CLFS_TARGET}-gcc ${CLFS_CFLAGS}" \ ./configure \ CROSS_COMPILE=${CLFS_TARGET}- \ --prefix=/ \ --target=${CLFS_TARGET}
Note that we're also setting the "CC" environment variable with our cross compiler and the CFLAGS we've defined at the start. Compiling and installing musl is just as straightforward:
user $ make user $ DESTDIR=${CLFS}/tools make install user $ popd
We have musl installed now! There's a couple of small issues though...
The first one is subtle and although it won't be apparent for a while, it will bite us later, so let's look into it right now. If we peek into the lib directory, we'll notice that something is off:
user $ pushd "${CLFS}"/tools/lib user $ ls -la total 3616 drwxr-xr-x 2 invakid404 invakid404 270 Feb 23 09:42 . drwxr-xr-x 4 invakid404 invakid404 32 Feb 23 09:42 .. -rw-r--r-- 1 invakid404 invakid404 1736 Feb 23 09:42 Scrt1.o -rw-r--r-- 1 invakid404 invakid404 2000 Feb 23 09:42 crt1.o -rw-r--r-- 1 invakid404 invakid404 1072 Feb 23 09:42 crti.o -rw-r--r-- 1 invakid404 invakid404 1016 Feb 23 09:42 crtn.o lrwxrwxrwx 1 invakid404 invakid404 12 Feb 23 09:42 ld-musl-aarch64.so.1 -> /lib/libc.so -rw-r--r-- 1 invakid404 invakid404 2709306 Feb 23 09:42 libc.a -rwxr-xr-x 1 invakid404 invakid404 937488 Feb 23 09:42 libc.so -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libcrypt.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libdl.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libm.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libpthread.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libresolv.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 librt.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libutil.a -rw-r--r-- 1 invakid404 invakid404 8 Feb 23 09:42 libxnet.a -rw-r--r-- 1 invakid404 invakid404 2648 Feb 23 09:42 rcrt1.o
The dynamic linker symlink is incorrectly pointing at "/lib/libc.so", while it should be pointing at the libc.so in the current directory. Thankfully, this is an easy fix:
user $ unlink ld-musl-aarch64.so.1 user $ ln -sf libc.so ld-musl-aarch64.so.1 user $ ls -la ld-musl-aarch64.so.1 lrwxrwxrwx 1 invakid404 invakid404 7 Feb 23 09:46 ld-musl-aarch64.so.1 -> libc.so
And now it's fine. Note that we made the symlink relative instead of absolute, that way it'll work when we chroot as well.
The other issue we'll run into in the next step is, as you might have noticed from the command output above, the startfiles (crt*.o) are installed in tools instead of cross-tools. This would result in a linker error while building libgcc, so we'll need to create some symlinks to those in cross-tools to get things to work properly:
user $ find -type f \ -iname 'crt*.o' \ -exec ln -sf ../../../tools/lib/{} "${CLFS}"/cross-tools/"${CLFS_TARGET}"/lib/{} \; user $ ls -la "${CLFS}"/cross-tools/"${CLFS_TARGET}"/lib total 16 drwxr-xr-x 3 invakid404 invakid404 65 Feb 23 10:00 . drwxr-xr-x 4 invakid404 invakid404 28 Feb 23 09:16 .. lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crt1.o -> ../../../tools/lib/./crt1.o lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crti.o -> ../../../tools/lib/./crti.o lrwxrwxrwx 1 invakid404 invakid404 27 Feb 23 10:00 crtn.o -> ../../../tools/lib/./crtn.o drwxr-xr-x 2 invakid404 invakid404 12288 Feb 23 09:16 ldscripts user $ popd
This should be sufficient to get GCC to build properly in the next step.
Final GCC
Having done that, we're now ready to build GCC against our freshly built musl. We already have everything fetched and set up correctly, so let's just wipe the "gcc-build" directory clean and start over:
user $ pushd gcc-build user $ rm -r *
We can now re-enable the long list of libraries we disabled when we built the static GCC, with a single exception: we'll need to keep libsanitizer disabled, since it doesn't seem to build correctly with musl due to the fact musl has no "fstab.h" header. Again, we'll use a mixture of what the embedded and regular CLFS books list as flags:
user $ AR=ar LDFLAGS="-Wl,-rpath,${CLFS}/cross-tools/lib" \ ../gcc-"${GCC_VERSION}"/configure \ --prefix=${CLFS}/cross-tools \ --build=${CLFS_HOST} \ --target=${CLFS_TARGET} \ --host=${CLFS_HOST} \ --with-sysroot=${CLFS} \ --with-local-prefix=${CLFS}/tools \ --with-native-system-header-dir=/tools/include \ --with-lib-path=${CLFS}/tools/lib \ --disable-nls \ --disable-static \ --enable-languages=c,c++ \ --disable-multilib \ --disable-libsanitizer \ --with-mpfr-include=$(pwd)/../gcc-"${GCC_VERSION}"/mpfr/src \ --with-mpfr-lib=$(pwd)/mpfr/src/.libs
Unlike the static GCC build, we have to build the entirety of GCC now:
user $ make AS_FOR_TARGET="${CLFS_TARGET}-as" \ LD_FOR_TARGET="${CLFS_TARGET}-ld" user $ make install user $ popd
Building the basic tools (Chapter 4.6)
Now, we're ready to start getting some tools cross-compiled for our target architecture. For the sake of completeness, we'll install everything listed under "Constructing a Temporary System" in the CLFS book, even if not everything we build is strictly necessary later.
Using the cross compiler
To use the cross compiler, we'll need to set some environment variables. I personally find it nice to spawn a new instance of "bash" to scope those variables to be able to get rid of them later:
user $ env \ TERM="${TERM}" \ CC="${CLFS_TARGET}-gcc ${CLFS_CFLAGS}" \ CXX="${CLFS_TARGET}-g++ ${CLFS_CFLAGS}" \ AR="${CLFS_TARGET}-ar" \ AS="${CLFS_TARGET}-as" \ RANLIB="${CLFS_TARGET}-ranlib" \ LD="${CLFS_TARGET}-ld" \ STRIP="${CLFS_TARGET}-strip" \ DYNAMIC_LINKER=$(eval echo "${CLFS}/tools/lib/ld-musl-*.so.1") \ bash --rcfile <(cat ~/.bashrc 2>/dev/null; echo 'PS1="(cross) ${PS1}"') user $ echo "${CC}" aarch64-unknown-linux-musl-gcc
The only non-standard variable in the list above is DYNAMIC_LINKER, which we'll need at the end of preparing the basic tools.
GMP
Unlike when building the cross compiler, we'll build and install GMP separately instead. We already have the GMP sources, so we'll just do a clean extract and get to building. The only thing we'll be consistently changing in the configure commands is the prefix, because we don't have a /tools symlink:
user $ tar xvf gmp-"${GMP_VERSION}".tar.xz user $ pushd gmp-"${GMP_VERSION}" user $ CC_FOR_BUILD=gcc \ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --enable-cxx user $ make user $ make install user $ popd
MPFR
Pretty much the same as above:
user $ tar xvf mpfr-"${MPFR_VERSION}".tar.xz user $ pushd mpfr-"${MPFR_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
MPC
Same as above:
user $ tar xvf mpc-"${MPC_VERSION}".tar.gz user $ pushd mpc-"${MPC_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
ISL
While this is an optional dependency and we skipped it earlier, we'll build it now, because why not :D
Nothing special, just fetch and unpack the sources, configure, build and install:
user $ export ISL_VERSION="0.24" user $ wget https://gcc.gnu.org/pub/gcc/infrastructure/isl-"${ISL_VERSION}".tar.bz2 user $ tar xvf isl-"${ISL_VERSION}".tar.bz2 user $ pushd isl-"${ISL_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Zlib
Nothing unusual yet again:
user $ export ZLIB_VERSION="1.2.11" user $ wget https://zlib.net/zlib-"${ZLIB_VERSION}".tar.gz user $ tar xvf zlib-"${ZLIB_VERSION}".tar.gz user $ pushd zlib-"${ZLIB_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools user $ make user $ make install user $ popd
Binutils
No special steps here either:
user $ pushd binutils-build user $ rm -rf * user $ ../binutils-${BINUTILS_VERSION}/configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --target=${CLFS_TARGET} \ --with-lib-path=${CLFS}/tools/lib \ --disable-nls \ --enable-shared \ --enable-64-bit-bfd \ --disable-multilib \ --enable-gold=yes \ --enable-plugins \ --with-system-zlib \ --enable-threads user $ make user $ make install user $ popd
GCC
This is where we'll need to do some patching to get things to work. We'll need to disable stdinc++ (refer to this bug):
user $ pushd gcc-"${GCC_VERSION}" user $ sed -i \ 's|^RAW_CXX_FOR_TARGET="$CXX_FOR_TARGET|& -nostdinc++|' \ configure user $ popd
We'll also need to disable libstdcxx-pch and libsanitizer for similar reasons as before:
user $ pushd gcc-build user $ rm -r * user $ ../gcc-"${GCC_VERSION}"/configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --target=${CLFS_TARGET} \ --with-local-prefix=${CLFS}/tools \ --disable-multilib \ --enable-languages=c,c++ \ --with-system-zlib \ --with-native-system-header-dir=/tools/include \ --disable-libssp \ --enable-install-libiberty \ --disable-libstdcxx-pch \ --disable-libsanitizer user $ make AS_FOR_TARGET="${AS}" \ LD_FOR_TARGET="${LD}" user $ make install user $ popd
Ncurses
We'll deviate from the CLFS book here as well. Let's fetch the sources first:
user $ export NCURSES_VERSION="6.3" user $ wget https://ftp.gnu.org/gnu/ncurses/ncurses-"${NCURSES_VERSION}".tar.gz user $ tar xvf ncurses-"${NCURSES_VERSION}".tar.gz user $ pushd ncurses-"${NCURSES_VERSION}"
First things first, we need to instruct ncurses to use the cross-compiled strip instead of the host one:
user $ sed -i -e "s/INSTALL_OPT_S=\\\"-s/& --strip-program=${STRIP}/" configure
Then, we'll need to build both ncurses and ncursesw since things will fail down the line if we don't. Let's build the regular one first. Note that we're also enabling terminfo-related flags to ensure things like clearing the terminal are going to work when we chroot:
user $ mkdir ncurses-build user $ pushd ncurses-build user $ ../configure \ --prefix=${CLFS}/tools \ --with-shared \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --without-debug \ --without-ada \ --enable-overwrite \ --with-build-cc=gcc \ --enable-overwrite \ --with-default-terminfo-dir=${CLFS}/tools/share/terminfo \ --with-termlib user $ make user $ make install user $ popd
Similarly, we'll build ncursesw as well. Note that we're changing the includedir as well, as that's where other packages are going to expect the headers to be:
user $ mkdir ncursesw-build user $ pushd ncursesw-build user $ ../configure \ --prefix=${CLFS}/tools \ --with-shared \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --without-debug \ --without-ada \ --enable-overwrite \ --with-build-cc=gcc \ --enable-widec \ --includedir=${CLFS}/tools/include/ncursesw \ --with-default-terminfo-dir=${CLFS}/tools/share/terminfo \ --with-termlib user $ make user $ make install user $ popd user $ popd
Bash
Nothing too interesting here either.
user $ export BASH_VERSION="5.1.16" user $ wget https://ftp.gnu.org/gnu/bash/bash-"${BASH_VERSION}".tar.gz user $ tar xvf bash-"${BASH_VERSION}".tar.gz user $ pushd bash-"${BASH_VERSION}" user $ cat > config.cache << "EOF" ac_cv_func_mmap_fixed_mapped=yes ac_cv_func_strcoll_works=yes ac_cv_func_working_mktime=yes bash_cv_func_sigsetjmp=present bash_cv_getcwd_malloc=yes bash_cv_job_control_missing=present bash_cv_printf_a_format=yes bash_cv_sys_named_pipes=present bash_cv_ulimit_maxfds=yes bash_cv_under_sys_siglist=yes bash_cv_unusable_rtsigs=no gt_cv_int_divbyzero_sigfpe=yes EOF user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --without-bash-malloc \ --cache-file=config.cache user $ make user $ make install user $ popd
Bzip2
Here we'll have to enable position-independent code (-fPIC), otherwise "file" won't link properly. Other than that, standard stuff:
user $ export BZIP2_VERSION="1.0.8" user $ wget https://www.sourceware.org/pub/bzip2/bzip2-"${BZIP2_VERSION}".tar.gz user $ tar xvf bzip2-"${BZIP2_VERSION}".tar.gz user $ pushd bzip2-"${BZIP2_VERSION}" user $ sed -i \ -e '/^all/s/ test$//' \ -e 's|CFLAGS=|&-fPIC |' \ Makefile user $ make CC="${CC}" AR="${AR}" RANLIB="${RANLIB}" user $ make PREFIX=${CLFS}/tools install user $ popd
Check
While check definitely isn't necessary, we'll install it anyway:
user $ export CHECK_VERSION="0.15.2" user $ wget https://github.com/libcheck/check/releases/download/"${CHECK_VERSION}"/check-"${CHECK_VERSION}".tar.gz user $ tar xvf check-"${CHECK_VERSION}".tar.gz user $ pushd check-"${CHECK_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --disable-subunit user $ make user $ make install user $ popd
Coreutils
Nothing unusual:
user $ export COREUTILS_VERSION="9.0" user $ wget https://ftp.gnu.org/gnu/coreutils/coreutils-"${COREUTILS_VERSION}".tar.xz user $ tar xvf coreutils-"${COREUTILS_VERSION}".tar.xz user $ pushd coreutils-"${COREUTILS_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --enable-install-program=hostname \ --cache-file=config.cache user $ sed -i -e 's/^man1_MANS/#man1_MANS/' Makefile user $ make user $ make install user $ popd
Diffutils
Same here:
user $ export DIFFUTILS_VERSION="3.8" user $ wget https://ftp.gnu.org/gnu/diffutils/diffutils-"${DIFFUTILS_VERSION}".tar.xz user $ tar xvf diffutils-"${DIFFUTILS_VERSION}".tar.xz user $ pushd diffutils-"${DIFFUTILS_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
File
We'll enable position-independent code explicitly here just in case, although it's enabled by default:
user $ export FILE_VERSION="5.41" user $ wget http://ftp.astron.com/pub/file/file-"${FILE_VERSION}".tar.gz user $ tar xvf file-"${FILE_VERSION}".tar.gz user $ pushd file-"${FILE_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --with-pic=yes user $ make user $ make install user $ popd
Findutils
The usual business here:
user $ export FINDUTILS_VERSION="4.9.0" user $ wget https://ftp.gnu.org/gnu/findutils/findutils-"${FINDUTILS_VERSION}".tar.xz user $ tar xvf findutils-"${FINDUTILS_VERSION}".tar.xz user $ pushd findutils-"${FINDUTILS_VERSION}" user $ echo "gl_cv_func_wcwidth_works=yes" > config.cache user $ echo "ac_cv_func_fnmatch_gnu=yes" >> config.cache user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --cache-file=config.cache user $ make user $ make install user $ popd
Gawk
Standard procedure here:
user $ export GAWK_VERSION="5.1.1" user $ wget https://ftp.gnu.org/gnu/gawk/gawk-"${GAWK_VERSION}".tar.xz user $ tar xvf gawk-"${GAWK_VERSION}".tar.xz user $ pushd gawk-"${GAWK_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Gettext
We'll install the entirety of gettext for simplicity:
user $ export GETTEXT_VERSION="0.21" user $ wget https://ftp.gnu.org/gnu/gettext/gettext-"${GETTEXT_VERSION}".tar.xz user $ tar xvf gettext-"${GETTEXT_VERSION}".tar.xz user $ pushd gettext-"${GETTEXT_VERSION}" user $ EMACS="no" \ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --disable-shared user $ make user $ make install user $ popd
Grep
Nothing special:
user $ export GREP_VERSION="3.7" user $ wget https://ftp.gnu.org/gnu/grep/grep-"${GREP_VERSION}".tar.xz user $ tar xvf grep-"${GREP_VERSION}".tar.xz user $ pushd grep-"${GREP_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Gzip
The usual business:
user $ export GZIP_VERSION="1.11" user $ wget https://ftp.gnu.org/gnu/gzip/gzip-"${GZIP_VERSION}".tar.gz user $ tar xvf gzip-"${GZIP_VERSION}".tar.gz user $ pushd gzip-"${GZIP_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Make
No differences here:
user $ export MAKE_VERSION="4.3" user $ wget https://ftp.gnu.org/gnu/make/make-"${MAKE_VERSION}".tar.gz user $ tar xvf make-"${MAKE_VERSION}".tar.gz user $ pushd make-"${MAKE_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --without-guile user $ make user $ make install user $ popd
Patch
The usual stuff:
user $ export PATCH_VERSION="2.7.6" user $ wget https://ftp.gnu.org/gnu/patch/patch-"${PATCH_VERSION}".tar.xz user $ tar xvf patch-"${PATCH_VERSION}".tar.xz user $ pushd patch-"${PATCH_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Sed
You've probably picked the pattern up already, nothing special here either:
user $ export SED_VERSION="4.8" user $ wget https://ftp.gnu.org/gnu/sed/sed-"${SED_VERSION}".tar.xz user $ tar xvf sed-"${SED_VERSION}".tar.xz user $ pushd sed-"${SED_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Tar
Same here:
user $ export TAR_VERSION="1.34" user $ wget https://ftp.gnu.org/gnu/tar/tar-"${TAR_VERSION}".tar.xz user $ tar xvf tar-"${TAR_VERSION}".tar.xz user $ pushd tar-"${TAR_VERSION}" user $ cat > config.cache << EOF gl_cv_func_wcwidth_works=yes gl_cv_func_btowc_eof=yes ac_cv_func_malloc_0_nonnull=yes gl_cv_func_mbrtowc_incomplete_state=yes gl_cv_func_mbrtowc_nul_retval=yes gl_cv_func_mbrtowc_null_arg1=yes gl_cv_func_mbrtowc_null_arg2=yes gl_cv_func_mbrtowc_retval=yes gl_cv_func_wcrtomb_retval=yes EOF user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --cache-file=config.cache user $ make user $ make install user $ popd
Texinfo
You've guessed it:
user $ export TEXINFO_VERSION="6.8" user $ wget https://ftp.gnu.org/gnu/texinfo/texinfo-"${TEXINFO_VERSION}".tar.xz user $ tar xvf texinfo-"${TEXINFO_VERSION}".tar.xz user $ pushd texinfo-"${TEXINFO_VERSION}" user $ PERL=/usr/bin/perl \ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Util-linux
Just as I was running out of things to say, we finally got to a point where we need to do some patching. For some reason, util-linux doesn't pick up the tinfow library automatically, so we need to add it to the LDFLAGS manually to avoid a "DSO missing from command line error":
user $ export UTIL_LINUX_VERSION="2.37.4" user $ wget https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v$(echo ${UTIL_LINUX_VERSION} | cut -d'.' -f1-2)/util-linux-"${UTIL_LINUX_VERSION}".tar.gz user $ tar xvf util-linux-"${UTIL_LINUX_VERSION}".tar.gz user $ pushd util-linux-"${UTIL_LINUX_VERSION}" user $ NCURSESW6_CONFIG=" " \ NCURSES6_CONFIG=" " \ NCURSESW5_CONFIG=" " \ NCURSES5_CONFIG=" " \ LDFLAGS="-ltinfow" \ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} \ --disable-bash-completion \ --disable-makeinstall-chown \ --disable-makeinstall-setuid \ --disable-nologin \ --without-python user $ make user $ make install user $ popd
Nano
Nano suffers from the same issue as util-linux. Thankfully, we know how to fix it:
user $ export NANO_VERSION="6.1" user $ wget https://ftp.gnu.org/gnu/nano/nano-"${NANO_VERSION}".tar.xz user $ tar xvf nano-"${NANO_VERSION}".tar.xz user $ pushd nano-"${NANO_VERSION}" user $ LDFLAGS="-ltinfow" \ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
xz-utils
Last, but not least, xz-utils requires no special attention to build:
user $ export XZ_UTILS_VERSION="5.2.5" user $ wget https://tukaani.org/xz/xz-"${XZ_UTILS_VERSION}".tar.gz user $ tar xvf xz-"${XZ_UTILS_VERSION}".tar.gz user $ pushd xz-"${XZ_UTILS_VERSION}" user $ ./configure \ --prefix=${CLFS}/tools \ --build=${CLFS_HOST} \ --host=${CLFS_TARGET} user $ make user $ make install user $ popd
Finishing touches
With all of that done, we're almost ready to chroot. There are two things to fix at this point:
- All ELF binaries expect the dynamic linker and runtime dependencies to be in / instead of /tools. We'll need to patch those if we want them to work.
- All pkgconfig files have a prefix that contains the absolute path to our CLFS directory. For those to work after chrooting, we'll need to strip it.
To address the first issue, this command utilizes the patchelf utility to set the dynamic linker (interpreter) and runtime dependency path (rpath):
user $ find "${CLFS}"/tools -type f -exec file {} + | \ grep ELF | \ cut -d':' -f1 | \ xargs -I{} patchelf \ --set-interpreter "${DYNAMIC_LINKER#$CLFS}" \ --set-rpath /tools/lib/ {} 2>/dev/null
We can verify that it works by checking some binary such as bash:
user $ file "${CLFS}"/tools/bin/bash /home/invakid404/clfs/tools/bin/bash: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /tools/lib/ld-musl-aarch64.so.1, with debug_info, not stripped
As you can see, the interpreter is now prefixed with /tools, as it should be.
To address the second issue, we can combine find and sed and strip all occurrences of the prefix from all pkgtools files:
user $ find ${CLFS}/tools -iname '*.pc' \ -exec sed -i -e "s|${CLFS}||g" {} \;
Chrooting (Chapter 4.8)
We should be all prepared for chrooting now. Let's start by fleshing out some directories and creating some devices as per the book:
In the command below, depending on how you 'switch to root', you may no longer have the ${CLFS}
variable set! This may require you to manually specify the path for the mknod
commands.
user $ mkdir -pv ${CLFS}/{dev,proc,run,sys} mkdir: created directory '/home/invakid404/clfs/dev' mkdir: created directory '/home/invakid404/clfs/proc' mkdir: created directory '/home/invakid404/clfs/run' mkdir: created directory '/home/invakid404/clfs/sys' root # mknod -m 600 ${CLFS}/dev/console c 5 1 root # mknod -m 666 ${CLFS}/dev/null c 1 3
We don't need to create the bind mounts manually, since fchroot does that for us. Speaking of fchroot, it's time to utilize it!
root # sudo fchroot . /tools/bin/bash Funtoo fchroot 0.2 ("Darth Elmo"); Copyright 2020-2022 Funtoo Solutions, Inc. Licensed under the Apache License, Version 2.0 >>> Entering arm-64bit (cortex-a53 CPU) fchroot... bash-5.1 #
And just like that, we've got a prompt! We won't be able to run anything yet though since /tools isn't in our path. Let's fix that:
Maybe I built in the wrong directory, but I had many more things in the chroot, due to all the source directories still existing there! -DR
bash-5.1 # export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin bash-5.1 # ls cross-tools dev etc proc run sources sys tools usr
Now we're talking! We'll need to export some more stuff, such as the terminal and terminfo location. I'll set "xterm" for my terminal since I use kitty and the kitty terminfo isn't installed:
bash-5.1 # export TERM="xterm" bash-5.1 # export TERMINFO="/tools/share/terminfo"
Now things like nano should work. Let's try to write a "hello world" program in C to verify that things are working:
bash-5.1 # nano hello.c #include <stdio.h> int main() { puts("Hello, world!"); return 0; } bash-5.1 # gcc -o hello hello.c Error loading shared library libstdc++.so.6: No such file or directory (needed by /tools/bin/gcc) Error loading shared library libgcc_s.so.1: No such file or directory (needed by /tools/bin/gcc) Error relocating /tools/bin/gcc: _ZSt20__throw_length_errorPKc: symbol not found Error relocating /tools/bin/gcc: _ZdaPvm: symbol not found Error relocating /tools/bin/gcc: _ZSt24__throw_out_of_range_fmtPKcz: symbol not found Error relocating /tools/bin/gcc: __cxa_guard_acquire: symbol not found Error relocating /tools/bin/gcc: _Znam: symbol not found Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm: symbol not found Error relocating /tools/bin/gcc: _ZdlPvm: symbol not found Error relocating /tools/bin/gcc: _ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base: symbol not found Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm: symbol not found Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_assignERKS4_: symbol not found Error relocating /tools/bin/gcc: __cxa_guard_release: symbol not found Error relocating /tools/bin/gcc: _Unwind_Backtrace: symbol not found Error relocating /tools/bin/gcc: _ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base: symbol not found Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEcm: symbol not found Error relocating /tools/bin/gcc: _Unwind_GetIPInfo: symbol not found Error relocating /tools/bin/gcc: _ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_: symbol not found Error relocating /tools/bin/gcc: _ZSt19__throw_logic_errorPKc: symbol not found Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE13find_first_ofEPKcmm: symbol not found Error relocating /tools/bin/gcc: _ZdaPv: symbol not found Error relocating /tools/bin/gcc: _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEPKcmm: symbol not found Error relocating /tools/bin/gcc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm: symbol not found Error relocating /tools/bin/gcc: _Znwm: symbol not found
Oh, well... This is fixable though:
bash-5.1 # pushd /tools bash-5.1 # mv lib64/* lib/ bash-5.1 # rmdir lib64 bash-5.1 # ln -sf lib lib64 bash-5.1 # popd
For some reason, some things were installed in /tools/lib, and others in /tools/lib64. To patch this, we moved everything into /tools/lib and linked them together.
We wouldn't have had this issue if we linked lib and lib64 in the first place, which is also a valid solution (this is how it's done on standard Funtoo systems). Might be worth looking into getting rid of this issue as a whole at some point in the future.
Let's try again now!
bash-5.1 # gcc -o hello hello.c
It builds! Let's try running it:
bash-5.1 # ./hello qemu-arm-64bit-wrapper: Could not open '/lib/ld-musl-aarch64.so.1': No such file or directory
Dynamic linker strikes again... Nothing that a symlink can't fix though:
bash-5.1 # mkdir lib bash-5.1 # ln -sf /tools/lib/ld-musl-aarch64.so.1 lib/
Let's try yet again:
bash-5.1 # ./hello Hello, world!
And it works!
What's next?
The next steps after this would be to try and get Python running, then Portage, then to kick off a build for a stage3.