Skip to content

Instantly share code, notes, and snippets.

@firasuke
Last active January 18, 2024 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save firasuke/82ee9ed032aaf61af26826e07a3db14d to your computer and use it in GitHub Desktop.
Save firasuke/82ee9ed032aaf61af26826e07a3db14d to your computer and use it in GitHub Desktop.
Building an x86-64 cross compiler targeting musl libc (musl-cross-make style)
#!/usr/bin/dash -e
# Copyright (c) 2020, Firas Khalil Khana
# Distributed under the terms of the ISC License
#
# Credits to Rich Felker and musl-cross-make for making this possible
#
set -e
umask 022
#
# Colors
#
BLUEC='\033[1;34m'
REDC='\033[1;31m'
GREENC='\033[1;32m'
NORMALC='\033[0m'
#
# Package Versions
#
binutils_ver=2.34
gcc_ver=10.1.0
gmp_ver=6.2.0
isl_ver=0.22.1
mpc_ver=1.1.0
mpfr_ver=4.0.2
musl_ver=1.2.0
#
# Package URLs (The usage of ftpmirror for GNU packages is preferred.)
#
binutils_url=https://ftpmirror.gnu.org/binutils/binutils-$binutils_ver.tar.lz
gcc_url=https://ftpmirror.gnu.org/gcc/gcc-$gcc_ver/gcc-$gcc_ver.tar.xz
gmp_url=https://ftpmirror.gnu.org/gmp/gmp-$gmp_ver.tar.lz
isl_url=http://isl.gforge.inria.fr/isl-$isl_ver.tar.xz
mpc_url=https://ftpmirror.gnu.org/mpc/mpc-$mpc_ver.tar.gz
mpfr_url=https://www.mpfr.org/mpfr-current/mpfr-$mpfr_ver.tar.xz
musl_url=https://www.musl-libc.org/releases/musl-$musl_ver.tar.gz
#
# Package Checksums (sha512sum)
#
binutils_sum=f4aadea1afa85d9ceb7be377afab9270a42ab0fd1fae86a7c69510b80de1aaac76f15cfb8730f9d233466a89fd020ab7e6e705e754c6b40f5fe2d16a5214562e
gcc_sum=0cb2a74c793face751f42bc580960b00e2bfea785872a0a2155f1f1dbfaa248f9591b67f4322db0f096f8844aca9243bc02732bda106c3b6e43b02bb67eb3096
gmp_sum=9975e8766e62a1d48c0b6d7bbdd2fccb5b22243819102ca6c8d91f0edd2d3a1cef21c526d647c2159bb29dd2a7dcbd0d621391b2e4b48662cf63a8e6749561cd
isl_sum=8dc7b0c14e5bfdca8f2161be51d3c9afcd18bc217bb19b7de01dbba0c6f3fdc2b725fb999f8562c77bf2918d3005c9247f7a58474a6da7697390067944d4d4aa
mpc_sum=72d657958b07c7812dc9c7cbae093118ce0e454c68a585bfb0e2fa559f1bf7c5f49b93906f580ab3f1073e5b595d23c6494d4d76b765d16dde857a18dd239628
mpfr_sum=d583555d08863bf36c89b289ae26bae353d9a31f08ee3894520992d2c26e5683c4c9c193d7ad139632f71c0a476d85ea76182702a98bf08dde7b6f65a54f8b88
musl_sum=58bd88189a6002356728cea1c6f6605a893fe54f7687595879add4eab283c8692c3b031eb9457ad00d1edd082cfe62fcc0eb5eb1d3bf4f1d749c0efa2a95fec1
#
# Development Directories
#
CURDIR="$PWD"
SRCDIR="$CURDIR/sources"
BLDDIR="$CURDIR/builds"
PCHDIR="$CURDIR/patches"
[ ! -d $SRCDIR ] && echo "${BLUEC}=>${NORMALC} Creating the sources directory...\n" && mkdir $SRCDIR
[ ! -d $BLDDIR ] && echo "${BLUEC}=>${NORMALC} Creating the builds directory...\n" && mkdir $BLDDIR
[ ! -d $PCHDIR ] && echo "${BLUEC}=>${NORMALC} Creating the patches directory...\n" && mkdir $PCHDIR
#
# Preparation Function - gtpackage()
#
gtpackage() {
cd $SRCDIR
if [ ! -d $1 ]; then
mkdir $1
else
echo "${REDC}=>${NORMALC} $1 source directory already exists, skipping..."
fi
cd $1
HOLDER="$(basename $2)"
if [ ! -f "$HOLDER" ]; then
echo "${GREENC}=>${NORMALC} Fetching "$HOLDER"..."
wget "$2"
else
echo "${REDC}=>${NORMALC} "$HOLDER" already exists, skipping..."
fi
echo "${GREENC}=>${NORMALC} Verifying "$HOLDER"..."
echo "$3 $HOLDER" | sha512sum -c || {
echo "${REDC}=>${NORMALC} "$HOLDER" is corrupted, redownloading..." &&
rm "$HOLDER" &&
wget "$2";
}
rm -fr $1-$4
echo "${GREENC}=>${NORMALC} Unpacking $HOLDER..."
tar xf $HOLDER -C .
echo
}
gtpackage binutils "$binutils_url" $binutils_sum $binutils_ver
gtpackage gcc "$gcc_url" $gcc_sum $gcc_ver
gtpackage gmp "$gmp_url" $gmp_sum $gmp_ver
gtpackage isl "$isl_url" $isl_sum $isl_ver
gtpackage mpc "$mpc_url" $mpc_sum $mpc_ver
gtpackage mpfr "$mpfr_url" $mpfr_sum $mpfr_ver
gtpackage musl "$musl_url" $musl_sum $musl_ver
#
# Patching
#
#
# The gcc patch is for a bug that forces CET when cross compiling in both lto-plugin
# and libiberty.
#
cd $PCHDIR
[ ! -d gcc ] && mkdir gcc
cd gcc
if [ ! -f Enable-CET-in-cross-compiler-if-possible.patch ]; then
echo "${GREENC}=>${NORMALC} Fetching gcc Enable-CET-in-cross-compiler-if-possible.patch from upstream..."
wget https://raw.githubusercontent.com/glaucuslinux/glaucus/master/cerata/gcc/patches/upstream/Enable-CET-in-cross-compiler-if-possible.patch
else
echo "${REDC}=>${NORMALC} Enable-CET-in-cross-compiler-if-possible.patch already exists, skipping..."
fi
echo "${BLUEC}=>${NORMALC} Applying gcc Enable-CET-in-cross-compiler-if-possible.patch from upstream..."
cd $SRCDIR/gcc/gcc-$gcc_ver
patch -p1 -i $PCHDIR/gcc/Enable-CET-in-cross-compiler-if-possible.patch
echo
cd $CURDIR
#
# Don't change anything from here on, unless you know what you're doing.
#
if [ -d "$CURDIR/builds" ]; then
echo "${GREENC}=>${NORMALC} Cleaning builds directory..."
rm -fr "$CURDIR/builds"
mkdir "$CURDIR/builds"
fi
if [ -d "$CURDIR/toolchain" ]; then
echo "${GREENC}=>${NORMALC} Cleaning toolchain directory..."
rm -fr "$CURDIR/toolchain"
mkdir "$CURDIR/toolchain"
fi
if [ -d "$CURDIR/bsysroot" ]; then
echo "${GREENC}=>${NORMALC} Cleaning bsysroot directory..."
rm -fr "$CURDIR/bsysroot"
fi
#
# We fetch the latest upstream config.guess to correctly determine the value of
# BUILD (`--build`).
#
if [ ! -f $CURDIR/config.guess ]; then
echo "${GREENC}=>${NORMALC} Fetching the latest config.guess from upstream..."
wget "https://raw.githubusercontent.com/glaucuslinux/glaucus/master/cerata/binutils/config.guess"
chmod +x config.guess
else
echo "${REDC}=>${NORMALC} Latest config.guess already exists, skipping..."
fi
echo "=> Determining --build..."
XBUILD="$($CURDIR/config.guess)"
echo "=> --build set to $XBUILD"
echo
#
# Since this is a cross compiler, we want `--host` to be equal to `--build`.
# Native toolchains are outside the scope of this script.
#
echo "=> Setting --host equal to --build since we're cross compiling..."
XHOST=$XBUILD
#
# Available Target Architectures
#
XTARGET=x86_64-linux-musl
#
# Build Directories (GTSYSROOT can be empty?)
#
GTBSYSROOT="$CURDIR/bsysroot"
GTSYSROOT="/$XTARGET"
GTOUTPUT="$CURDIR/output"
#
# Make command
#
MAKE="make MULTILIB_OSDIRNAMES= INFO_DEPS= infodir= ac_cv_prog_lex_root=lex.yy MAKEINFO=true"
#
# FLAGS
#
CFLAGS="-O2"
CXXFLAGS="-O2"
#
# Uncomment these for a lighter toolchain.
#
#CFLAGS="-g0 -Os"
#CXXFLAGS="-g0 -Os"
#LDFLAGS="-s"
#
# Step 1: Configuring binutils
#
echo "=> Preparing binutils..."
cd $BLDDIR
mkdir binutils && cd binutils
#
# Notice how `--build` and `--host` are equal because we're "purely" cross
# compiling to `--target`.
#
# Note the use of an empty `--prefix`, and later the use of `DESTDIR` for
# installation.
#
echo "=> Configuring binutils..."
$SRCDIR/binutils/binutils-$binutils_ver/configure \
--build=$XBUILD \
--host=$XBUILD \
--target=$XTARGET \
--prefix= \
--libdir=/lib \
--with-sysroot=$GTSYSROOT \
--disable-multilib \
--disable-separate-code \
--disable-werror \
--enable-deterministic-archives
echo
#
# Step 2: Building binutils
#
echo "=> Building binutils..."
$MAKE \
all
echo
#
# Step 3: GCC Prerequisites
#
# We track them manually instead of using `contrib/download_prerequisites` in
# gcc's sources.
#
echo "=> Preparing GCC prerequisites..."
cp -ar $SRCDIR/gmp/gmp-$gmp_ver $SRCDIR/gcc/gcc-$gcc_ver/gmp
cp -ar $SRCDIR/mpfr/mpfr-$mpfr_ver $SRCDIR/gcc/gcc-$gcc_ver/mpfr
cp -ar $SRCDIR/mpc/mpc-$mpc_ver $SRCDIR/gcc/gcc-$gcc_ver/mpc
cp -ar $SRCDIR/isl/isl-$isl_ver $SRCDIR/gcc/gcc-$gcc_ver/isl
echo
#
# Step 4: Configuring gcc
#
echo "=> Preparing gcc..."
cd $BLDDIR
mkdir gcc && cd gcc
#
# Again, everything said in binutils applies here.
#
# We need c++ language support to be able to build GCC, since GCC has big parts
# of its source code written in C++.
#
echo "=> Configuring gcc..."
AR_FOR_TARGET=$BLDDIR/binutils/binutils/ar \
AS_FOR_TARGET=$BLDDIR/binutils/gas/as-new \
LD_FOR_TARGET=$BLDDIR/binutils/ld/ld-new \
NM_FOR_TARGET=$BLDDIR/binutils/binutils/nm-new \
OBJCOPY_FOR_TARGET=$BLDDIR/binutils/binutils/objcopy \
OBJDUMP_FOR_TARGET=$BLDDIR/binutils/binutils/objdump \
RANLIB_FOR_TARGET=$BLDDIR/binutils/binutils/ranlib \
READELF_FOR_TARGET=$BLDDIR/binutils/binutils/readelf \
STRIP_FOR_TARGET=$BLDDIR/binutils/binutils/strip-new \
$SRCDIR/gcc/gcc-$gcc_ver/configure \
--build=$XBUILD \
--host=$XBUILD \
--target=$XTARGET \
--prefix= \
--libdir=/lib \
--with-build-sysroot=$GTBSYSROOT \
--with-sysroot=$GTSYSROOT \
--enable-languages=c,c++ \
--disable-bootstrap \
--disable-assembly \
--disable-werror \
--disable-multilib \
--enable-tls \
--disable-libsanitizer \
--disable-gnu-indirect-function \
--enable-libstdcxx-time=rt
echo
#
# Step 5: Preparing the build sysroot $GTBSYSROOT
#
echo "=> Preparing the build sysroot..."
mkdir $GTBSYSROOT
#
# This symlink is basically a hack but it's needed...
#
# You can get rid of the symlink ($GTBSYSROOT/usr -> .) by:
#
# 1- Modifying the native include dir for gcc, by passing
# `--with-native-system-header-dir=/include` (firasuke)
#
# 2- Modifying `lib64` to `../../lib` and not `../lib` in the `t-linux64` file
# since $XTARGET/bin/ld is called before bin/$XTARGET-ld (firasuke)
#
ln -fnsv . $GTBSYSROOT/usr
#
# This symlink (lib32 -> lib) shouldn't be needed (ideally we prefer that lib
# stands for what was previously called lib64, and lib32 stands for lib32), but
# mcm does it, so we do it...
#
ln -fnsv lib $GTBSYSROOT/lib32
#
# This is needed for some 64-bit Glibc hosts that might create a lib64 directory
# and install some GCC stuff there like libcc1 (firasuke)
#
ln -fnsv lib $GTBSYSROOT/lib64
mkdir -p $GTBSYSROOT/include
echo
#
# Step 6: Building gcc
#
echo "=> Building gcc..."
cd $BLDDIR/gcc
$MAKE \
all-gcc
echo
#
# Step 7: Configuring musl
#
echo "=> Preparing musl..."
cd $BLDDIR
mkdir musl && cd musl
#
# Again, empty prefix for musl. Also notice how `--host` here is set equal to
# `--target` (this is the de facto here, and you'd get the same results with
# `--target`.
#
echo "=> Configuring musl..."
CC="../gcc/gcc/xgcc -B ../gcc/gcc" \
LIBCC="../gcc/$XTARGET/libgcc/libgcc.a" \
$SRCDIR/musl/musl-$musl_ver/configure \
--host=$XTARGET \
--prefix=
echo
#
# Step 8: Installing musl headers to GTBSYSROOT
#
# With musl installs, you almost always should use a DESTDIR (that also should
# 99% be equal to gcc's and binutils `--with-sysroot` value, unless you know
# what you're doing...
#
echo "=> Installing musl-headers..."
$MAKE \
DESTDIR=$GTBSYSROOT \
install-headers
echo
#
# Step 9: Building gcc (libgcc)
#
cd $BLDDIR/gcc
$MAKE \
enable_shared=no \
all-target-libgcc
echo
#
# Step 10: Building musl
#
echo "=> Building musl..."
cd $BLDDIR/musl
$MAKE \
AR=$BLDDIR/binutils/binutils/ar \
RANLIB=$BLDDIR/binutils/binutils/ranlib
echo
#
# Step 11: Installing musl to GTBSYSROOT
#
echo "=> Installing musl to $GTBSYSROOT..."
$MAKE \
AR=$BLDDIR/binutils/binutils/ar \
RANLIB=$BLDDIR/binutils/binutils/ranlib \
DESTDIR=$GTBSYSROOT \
install
echo
#
# Step 12: Building gcc final
#
echo "=> Building gcc final..."
cd $BLDDIR/gcc
$MAKE
echo
#
# Step 13: Installing musl to GTSYSROOT
#
echo "=> Installing musl to GTSYSROOT..."
cd $BLDDIR/musl
$MAKE \
AR=$BLDDIR/binutils/binutils/ar \
RANLIB=$BLDDIR/binutils/binutils/ranlib \
DESTDIR=$GTOUTPUT/$GTSYSROOT \
install
echo
#
# Step 14: Installing gcc
#
echo "=> Installing gcc..."
cd $BLDDIR/gcc
$MAKE \
DESTDIR=$GTOUTPUT \
install
#
# Because many programs expect `cc`/`$XTARGET-cc` as their C compiler instead of
# explicitly specifying gcc.
#
ln -fnsv $XTARGET-gcc $GTOUTPUT/bin/$XTARGET-cc
#
# Step 15: Installing binutils
#
echo "=> Installing binutils..."
cd $BLDDIR/binutils
$MAKE \
DESTDIR=$GTOUTPUT \
install
echo "=> Done! Have fun!"
@firasuke
Copy link
Author

firasuke commented Jun 17, 2020

This script is broken, but I'm leaving it here for reference. Maybe I'll fix it when I have enough time, but who knows...

I recommend that you use mussel instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment